[
  {
    "path": ".gitignore",
    "content": "__pycache__/\n*.py[cod]\n*$py.class\n\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM python:3.9.15\n\nWORKDIR /app\nCOPY requirements.txt /app\nRUN pip3 install  -i https://mirrors.aliyun.com/pypi/simple/  --trusted-host mirrors.aliyun.com  -r requirements.txt --no-cache-dir\nCOPY . /app \nEXPOSE 8000\nENTRYPOINT [\"python3\"] \nCMD [\"server.py\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# FreeAskInternet\n\n## 🎉🎉🎉 Yeah we have a logo now! 🎉🎉🎉\n\n![lgoo](./doc/logo-20240412.png)\n\n> Running www.perplexity.ai like app complete FREE, LOCAL, PRIVATE and NO GPU NEED on any computer\n> [!IMPORTANT]  \n> **If you are unable to use this project normally, it is most likely due to issues with your internet connection or your IP, you need free internet connection to use this project normally. 如果您无法正常使用此项目，很可能是由于您的 IP 存在问题，或者你不能自由访问互联网。**\n\n## What is FreeAskInternet\n\nFreeAskInternet is a completely free, private and locally running search aggregator & answer generate using LLM, Without GPU needed. The user can ask a question and the system will use searxng to make a multi engine search and combine the search result to the ChatGPT3.5 LLM and generate the answer based on search results. All process running locally and  No GPU or OpenAI or Google API keys are needed.\n\n## Features\n\n- 🈚️ Completely FREE (no need for any API keys)\n- 💻 Completely LOCAL (no GPU need, any computer can run )\n- 🔐 Completely PRIVATE (all thing running locally, using custom llm)\n- 👻 Runs WITHOUT LLM Hardware (NO GPU NEED!)\n- 🤩 Using Free ChatGPT3.5 / Qwen / Kimi / ZhipuAI(GLM) API (NO API keys need! Thx OpenAI)\n- 🐵 Custom LLM(ollama,llama.cpp) support, Yes we love ollama!\n- 🚀 Fast and easy to deploy with Docker Compose\n- 🌐 Web and Mobile friendly interface, designed for Web Search enhanced AI Chat, allowing for easy access from any device.\n\n## Screenshots\n\n1. index:\n\n![index](./doc/index.png)\n\n2. Search based AI Chat:\n\n![index](./doc/Screenshot-4.png)\n\n3. Multi LLM models and custom LLM like ollama support:\n\n![index](./doc/Screenshot-5.png)\n\n## How It Works?\n\n1. System get user input question in FreeAskInternet UI interface( running locally), and call searxng (running locally) to make search on multi search engine.\n2. crawl search result links content and pass to ChatGPT3.5 / Kimi / Qwen / ZhipuAI / ollama (by using custom llm), ask LLM to answer user question based on this contents as references.\n3. Stream the answer to Chat UI.\n4. We support custom LLM setting, so theoretically infinite llm support.\n\n## Status\n\nThis project is still in its very early days. Expect some bugs.\n\n### Run the latest release\n\n```bash\ngit clone https://github.com/nashsu/FreeAskInternet.git\ncd ./FreeAskInternet\ndocker-compose up -d \n```\n\n🎉 You should now be able to open the web interface on http://localhost:3000. Nothing else is exposed by default.( For old web interface, accessing  http://localhost:3030)\n\n## How to get and set Kimi / Qwen / ZhipuAI Token?\n\nHow to get Token?\n\nWe are using [https://github.com/LLM-Red-Team](https://github.com/LLM-Red-Team) projects to provide those service, you can reference to their readme.\n\nReference : [https://github.com/LLM-Red-Team/kimi-free-api](https://github.com/LLM-Red-Team/kimi-free-api)\n\n![setting token](./doc/Screenshot-3.png)\n\n## How to using custom LLM like ollama? (Yes we love ollama)\n\n1. start ollama serve\n\n```bash\nexport OLLAMA_HOST=0.0.0.0\nollama serve\n```\n\n2. set ollama url in setting:\nYou MUST using your computer's ip address, not localhost/127.0.0.1, because in docker you can't access this address.\nThe model name is the model you want to serve by ollama.\n![setting custom llm url](./doc/Screenshot-2.png)\n\nollama model Reference : [https://ollama.com/library](https://ollama.com/library)\n\n### How to update to latest\n\n```bash\ncd ./FreeAskInternet\ngit pull\ndocker compose down\ndocker compose rm backend\ndocker compose rm free_ask_internet_ui\ndocker image rm nashsu/free_ask_internet\ndocker image rm nashsu/free_ask_internet_ui\ndocker-compose up -d\n```\n\n## Credits\n\n- ChatGPT-Next-Web : [https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web)\n- FreeGPT35: [https://github.com/missuo/FreeGPT35](https://github.com/missuo/FreeGPT35)\n- Kimi\\Qwen\\ZhipuAI [https://github.com/LLM-Red-Team](https://github.com/LLM-Red-Team)\n- searxng: [https://github.com/searxng/searxng](https://github.com/searxng/searxng)\n\n## Special thanks to our logo designer\n\n[AdlerMurcus](https://github.com/AdlerMurcus)\n\n<a href=\"https://github.com/AdlerMurcus\">\n  <img src=\"https://avatars.githubusercontent.com/u/40649955?v=4\" width=\"100\" height=\"100\" class=\"avatar avatar-user width-full border color-bg-default\"/>\n</a>\n\n## License\n\nApache-2.0 license\n\n## Star History\n\n[![Star History Chart](https://api.star-history.com/svg?repos=nashsu/FreeAskInternet&type=Date)](https://star-history.com/#nashsu/FreeAskInternet&Date)\n"
  },
  {
    "path": "docker-compose.yaml",
    "content": "services:\n  backend:\n    image: docker.io/nashsu/free_ask_internet:latest\n    depends_on:\n      - llm-freegpt35\n    restart: on-failure\n \n\n  freeaskinternet-ui:\n    image: docker.io/nashsu/free_ask_internet_ui:latest\n    ports:\n      - \"3000:80\"\n    environment:\n      BACKEND_HOST: \"backend:8000\"\n    depends_on:\n      - backend\n    restart: always\n\n  chatgpt-next-web:\n    image: yidadaa/chatgpt-next-web\n    ports:\n      - \"3030:3000\"\n    environment:\n      OPENAI_API_KEY: \"FreeAskInternet\"\n      # CODE: \"FreeAskInternet\" # 如果你想要设置页面的访问密码，请修改这里\n      BASE_URL: \"http://backend:8000\"\n      CUSTOM_MODELS: \"-all,+gpt-3.5-turbo\"\n    depends_on:\n      - llm-freegpt35\n    restart: always\n\n  llm-freegpt35:\n    image: missuo/freegpt35:latest\n    restart: always\n\n  llm-kimi:\n    image: vinlic/kimi-free-api:latest\n    restart: always\n    environment:\n      - TZ=Asia/Shanghai\n\n  llm-glm4:\n    image: vinlic/glm-free-api:latest\n    restart: always\n    environment:\n      - TZ=Asia/Shanghai\n\n\n  llm-qwen:\n    image: vinlic/qwen-free-api:latest\n    restart: always\n    environment:\n      - TZ=Asia/Shanghai\n\n  searxng:\n    image: docker.io/searxng/searxng:latest\n    volumes:\n      - ./searxng:/etc/searxng:rw\n    environment:\n      - SEARXNG_BASE_URL=https://${SEARXNG_HOSTNAME:-localhost}/\n    cap_drop:\n      - ALL\n    cap_add:\n      - CHOWN\n      - SETGID\n      - SETUID\n    logging:\n      driver: 'json-file'\n      options:\n        max-size: '1m'\n        max-file: '1'\n    restart: always\n"
  },
  {
    "path": "free_ask_internet.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport json\nimport os \nfrom pprint import pprint\nimport requests\nimport trafilatura\nfrom trafilatura import bare_extraction\nfrom concurrent.futures import ThreadPoolExecutor\nimport concurrent\nimport requests\nimport openai\nimport time \nfrom datetime import datetime\nfrom urllib.parse import urlparse\nimport tldextract\nimport platform\nimport urllib.parse\n\n \ndef extract_url_content(url):\n    downloaded = trafilatura.fetch_url(url)\n    content =  trafilatura.extract(downloaded)\n    \n    return {\"url\":url, \"content\":content}\n\n\n \n\ndef search_web_ref(query:str, debug=False):\n \n    content_list = []\n\n    try:\n\n        safe_string = urllib.parse.quote_plus(\":all !general \" + query)\n\n        response = requests.get('http://searxng:8080?q=' + safe_string + '&format=json')\n        response.raise_for_status()\n        search_results = response.json()\n \n        if debug:\n            print(\"JSON Response:\")\n            pprint(search_results)\n        pedding_urls = []\n\n        conv_links = []\n\n        if search_results.get('results'):\n            for item in search_results.get('results')[0:9]:\n                name = item.get('title')\n                snippet = item.get('content')\n                url = item.get('url')\n                pedding_urls.append(url)\n\n                if url:\n                    url_parsed = urlparse(url)\n                    domain = url_parsed.netloc\n                    icon_url =  url_parsed.scheme + '://' + url_parsed.netloc + '/favicon.ico'\n                    site_name = tldextract.extract(url).domain\n \n                conv_links.append({\n                    'site_name':site_name,\n                    'icon_url':icon_url,\n                    'title':name,\n                    'url':url,\n                    'snippet':snippet\n                })\n\n            results = []\n            futures = []\n\n            executor = ThreadPoolExecutor(max_workers=10) \n            for url in pedding_urls:\n                futures.append(executor.submit(extract_url_content,url))\n            try:\n                for future in futures:\n                    res = future.result(timeout=5)\n                    results.append(res)\n            except concurrent.futures.TimeoutError:\n                print(\"任务执行超时\")\n                executor.shutdown(wait=False,cancel_futures=True)\n\n            for content in results:\n                if content and content.get('content'):\n                    \n                    item_dict = {\n                        \"url\":content.get('url'),\n                        \"content\": content.get('content'),\n                        \"length\":len(content.get('content'))\n                    }\n                    content_list.append(item_dict)\n                if debug:\n                    print(\"URL: {}\".format(url))\n                    print(\"=================\")\n \n        return  conv_links,content_list\n    except Exception as ex:\n        raise ex\n\n\ndef gen_prompt(question,content_list, lang=\"zh-CN\", context_length_limit=11000,debug=False):\n    \n    limit_len = (context_length_limit - 2000)\n    if len(question) > limit_len:\n        question = question[0:limit_len]\n    \n    ref_content = [ item.get(\"content\") for item in content_list]\n    \n    answer_language = ' Simplified Chinese '\n    if lang == \"zh-CN\":\n        answer_language = ' Simplified Chinese '\n    if lang == \"zh-TW\":\n        answer_language = ' Traditional Chinese '\n    if lang == \"en-US\":\n        answer_language = ' English '\n\n\n    if len(ref_content) > 0:\n        \n        if False:\n            prompts = '''\n            您是一位由 nash_su 开发的大型语言人工智能助手。您将被提供一个用户问题，并需要撰写一个清晰、简洁且准确的答案。提供了一组与问题相关的上下文，每个都以[[citation:x]]这样的编号开头，x代表一个数字。请在适当的情况下在句子末尾引用上下文。答案必须正确、精确，并以专家的中立和职业语气撰写。请将答案限制在2000个标记内。不要提供与问题无关的信息，也不要重复。如果给出的上下文信息不足，请在相关主题后写上“信息缺失：”。请按照引用编号[citation:x]的格式在答案中对应部分引用上下文。如果一句话源自多个上下文，请列出所有相关的引用编号，例如[citation:3][citation:5]，不要将引用集中在最后返回，而是在答案对应部分列出。除非是代码、特定的名称或引用编号，答案的语言应与问题相同。以下是上下文的内容集：\n            '''  + \"\\n\\n\" + \"```\" \n            ref_index = 1\n\n            for ref_text in ref_content:\n                \n                prompts = prompts + \"\\n\\n\" + \" [citation:{}]  \".format(str(ref_index)) +  ref_text\n                ref_index += 1\n\n            if len(prompts) >= limit_len:\n                prompts = prompts[0:limit_len]        \n            prompts = prompts + '''\n    ```\n    记住，不要一字不差的重复上下文内容. 回答必须使用简体中文，如果回答很长，请尽量结构化、分段落总结。请按照引用编号[citation:x]的格式在答案中对应部分引用上下文。如果一句话源自多个上下文，请列出所有相关的引用编号，例如[citation:3][citation:5]，不要将引用集中在最后返回，而是在答案对应部分列出。下面是用户问题：\n    ''' + question  \n        else:\n            prompts = '''\n            You are a large language AI assistant develop by nash_su. You are given a user question, and please write clean, concise and accurate answer to the question. You will be given a set of related contexts to the question, each starting with a reference number like [[citation:x]], where x is a number. Please use the context and cite the context at the end of each sentence if applicable.\n            Your answer must be correct, accurate and written by an expert using an unbiased and professional tone. Please limit to 1024 tokens. Do not give any information that is not related to the question, and do not repeat. Say \"information is missing on\" followed by the related topic, if the given context do not provide sufficient information.\n\n            Please cite the contexts with the reference numbers, in the format [citation:x]. If a sentence comes from multiple contexts, please list all applicable citations, like [citation:3][citation:5]. Other than code and specific names and citations, your answer must be written in the same language as the question.\n            Here are the set of contexts:\n            '''  + \"\\n\\n\" + \"```\" \n            ref_index = 1\n\n            for ref_text in ref_content:\n                \n                prompts = prompts + \"\\n\\n\" + \" [citation:{}]  \".format(str(ref_index)) +  ref_text\n                ref_index += 1\n\n            if len(prompts) >= limit_len:\n                prompts = prompts[0:limit_len]        \n            prompts = prompts + '''\n            ```\n            Above is the reference contexts. Remember, don't repeat the context word for word. Answer in ''' + answer_language + '''. If the response is lengthy, structure it in paragraphs and summarize where possible. Cite the context using the format [citation:x] where x is the reference number. If a sentence originates from multiple contexts, list all relevant citation numbers, like [citation:3][citation:5]. Don't cluster the citations at the end but include them in the answer where they correspond.\n            Remember, don't blindly repeat the contexts verbatim. And here is the user question:\n            ''' + question  \n \n     \n    else:\n        prompts = question\n\n    if debug:\n        print(prompts)\n        print(\"总长度：\"+ str(len(prompts)))\n    return prompts\n\n\ndef chat(prompt, model:str,llm_auth_token:str,llm_base_url:str,using_custom_llm=False,stream=True, debug=False):\n    openai.base_url = \"http://127.0.0.1:3040/v1/\"\n\n    if model == \"gpt3.5\":\n        openai.base_url = \"http://llm-freegpt35:3040/v1/\"\n    \n    if model == \"kimi\":\n        openai.base_url = \"http://llm-kimi:8000/v1/\"\n    if model == \"glm4\":\n        openai.base_url = \"http://llm-glm4:8000/v1/\"\n    if model == \"qwen\":\n        openai.base_url = \"http://llm-qwen:8000/v1/\"\n    \n\n    if llm_auth_token == '':\n        llm_auth_token = \"CUSTOM\"\n        \n    openai.api_key = llm_auth_token\n\n    if using_custom_llm:\n        openai.base_url = llm_base_url\n        openai.api_key = llm_auth_token\n\n\n    total_content = \"\"\n    for chunk in openai.chat.completions.create(\n        model=model,\n        messages=[{\n            \"role\": \"user\",\n            \"content\": prompt\n        }],\n        stream=True,\n        max_tokens=1024,temperature=0.2\n    ):\n        stream_resp = chunk.dict()\n        token = stream_resp[\"choices\"][0][\"delta\"].get(\"content\", \"\")\n        if token:\n            \n            total_content += token\n            yield token\n    if debug:\n        print(total_content)\n \n\n \n    \ndef ask_internet(query:str,  debug=False):\n  \n    content_list = search_web_ref(query,debug=debug)\n    if debug:\n        print(content_list)\n    prompt = gen_prompt(query,content_list,context_length_limit=6000,debug=debug)\n    total_token =  \"\"\n \n    for token in chat(prompt=prompt):\n    # for token in daxianggpt.chat(prompt=prompt):\n        if token:\n            total_token += token\n            yield token\n    yield \"\\n\\n\"\n    # 是否返回参考资料\n    if True:\n        yield \"---\"\n        yield \"\\n\"\n        yield \"参考资料:\\n\"\n        count = 1\n        for url_content in content_list:\n            url = url_content.get('url')\n            yield \"*[{}. {}]({})*\".format(str(count),url,url )  \n            yield \"\\n\"\n            count += 1\n "
  },
  {
    "path": "requirements.txt",
    "content": "annotated-types==0.6.0\nanyio==4.3.0\ncertifi==2024.2.2\ncharset-normalizer==3.3.2\nclick==8.1.7\ncourlan==1.0.0\ndateparser==1.2.0\ndistro==1.9.0\nexceptiongroup==1.2.0\nfastapi==0.110.1\nfilelock==3.13.3\nh11==0.14.0\nhtmldate==1.8.0\nhttpcore==1.0.5\nhttpx==0.27.0\nidna==3.6\njusText==3.0.0\nlangcodes==3.3.0\nlxml==5.1.1\nopenai==1.16.2\npydantic==2.6.4\npydantic_core==2.16.3\npython-dateutil==2.9.0.post0\npytz==2024.1\nregex==2023.12.25\nrequests==2.31.0\nrequests-file==2.0.0\nsix==1.16.0\nsniffio==1.3.1\nsse-starlette==2.0.0\nstarlette==0.37.2\ntld==0.13\ntldextract==5.1.2\ntqdm==4.66.2\ntrafilatura==1.8.1\ntyping_extensions==4.10.0\ntzlocal==5.2\nurllib3==2.2.1\nuvicorn==0.29.0\n"
  },
  {
    "path": "searxng/settings.yml",
    "content": "general:\n  # Debug mode, only for development. Is overwritten by ${SEARXNG_DEBUG}\n  debug: false\n  # displayed name\n  instance_name: \"free_ask_internet\"\n  # For example: https://example.com/privacy\n  privacypolicy_url: false\n  # use true to use your own donation page written in searx/info/en/donate.md\n  # use false to disable the donation link\n  donation_url: false\n  # mailto:contact@example.com\n  contact_url: false\n  # record stats\n  enable_metrics: true\n\nbrand:\n  new_issue_url: https://github.com/searxng/searxng/issues/new\n  docs_url: https://docs.searxng.org/\n  public_instances: https://searx.space\n  wiki_url: https://github.com/searxng/searxng/wiki\n  issue_url: https://github.com/searxng/searxng/issues\n  # custom:\n  #   maintainer: \"Jon Doe\"\n  #   # Custom entries in the footer: [title]: [link]\n  #   links:\n  #     Uptime: https://uptime.searxng.org/history/darmarit-org\n  #     About: \"https://searxng.org\"\n\nsearch:\n  # Filter results. 0: None, 1: Moderate, 2: Strict\n  safe_search: 0\n  # Existing autocomplete backends: \"dbpedia\", \"duckduckgo\", \"google\", \"yandex\", \"mwmbl\",\n  # \"seznam\", \"startpage\", \"stract\", \"swisscows\", \"qwant\", \"wikipedia\" - leave blank to turn it off\n  # by default.\n  autocomplete: \"\"\n  # minimun characters to type before autocompleter starts\n  # Default search language - leave blank to detect from browser information or\n  # use codes from 'languages.py'\n  default_lang: \"auto\"\n  ban_time_on_fail: 1\n  max_ban_time_on_fail: 120\n  suspended_times:\n    SearxEngineAccessDenied: 86400\n    SearxEngineCaptcha: 86400\n    SearxEngineTooManyRequests: 3600\n    cf_SearxEngineCaptcha: 1296000\n    cf_SearxEngineAccessDenied: 86400\n    recaptcha_SearxEngineCaptcha: 604800\n  formats:\n    - html\n    - json\n\nserver:\n  # Is overwritten by ${SEARXNG_PORT} and ${SEARXNG_BIND_ADDRESS}\n  port: 8080\n  bind_address: \"0.0.0.0\"\n  # public URL of the instance, to ensure correct inbound links. Is overwritten\n  # by ${SEARXNG_URL}.\n  base_url: http://localhost:/  # \"http://example.com/location\"\n  limiter: false  # rate limit the number of request on the instance, block some bots\n  public_instance: false  # enable features designed only for public instances\n\n  # If your instance owns a /etc/searxng/settings.yml file, then set the following\n  # values there.\n\n  secret_key: \"f884dadd9e6b23ea6c073a031b18daa0288ff1d82a1c5514089f0ecb32ac7a54\"  # Is overwritten by ${SEARXNG_SECRET}\n  # Proxying image results through searx\n  image_proxy: false\n  # 1.0 and 1.1 are supported\n  http_protocol_version: \"1.0\"\n  # POST queries are more secure as they don't show up in history but may cause\n  # problems when using Firefox containers\n  method: \"POST\"\n  default_http_headers:\n    X-Content-Type-Options: nosniff\n    X-Download-Options: noopen\n    X-Robots-Tag: noindex, nofollow\n    Referrer-Policy: no-referrer\n\nredis:\n  # URL to connect redis database. Is overwritten by ${SEARXNG_REDIS_URL}.\n  # https://docs.searxng.org/admin/settings/settings_redis.html#settings-redis\n  url: false\n\nui:\n  # Custom static path - leave it blank if you didn't change\n  static_path: \"\"\n  static_use_hash: false\n  # Custom templates path - leave it blank if you didn't change\n  templates_path: \"\"\n  # query_in_title: When true, the result page's titles contains the query\n  # it decreases the privacy, since the browser can records the page titles.\n  query_in_title: false\n  # infinite_scroll: When true, automatically loads the next page when scrolling to bottom of the current page.\n  infinite_scroll: false\n  # ui theme\n  default_theme: simple\n  # center the results ?\n  center_alignment: false\n  # URL prefix of the internet archive, don't forget trailing slash (if needed).\n  # cache_url: \"https://webcache.googleusercontent.com/search?q=cache:\"\n  # Default interface locale - leave blank to detect from browser information or\n  # use codes from the 'locales' config section\n  default_locale: \"\"\n  # Open result links in a new tab by default\n  # results_on_new_tab: false\n  theme_args:\n    # style of simple theme: auto, light, dark\n    simple_style: auto\n  # Perform search immediately if a category selected.\n  # Disable to select multiple categories at once and start the search manually.\n  search_on_category_select: true\n  # Hotkeys: default or vim\n  hotkeys: default\n\n# Lock arbitrary settings on the preferences page.  To find the ID of the user\n# setting you want to lock, check the ID of the form on the page \"preferences\".\n#\n# preferences:\n#   lock:\n#     - language\n#     - autocomplete\n#     - method\n#     - query_in_title\n\n# searx supports result proxification using an external service:\n# https://github.com/asciimoo/morty uncomment below section if you have running\n# morty proxy the key is base64 encoded (keep the !!binary notation)\n# Note: since commit af77ec3, morty accepts a base64 encoded key.\n#\n# result_proxy:\n#   url: http://127.0.0.1:3000/\n#   # the key is a base64 encoded string, the YAML !!binary prefix is optional\n#   key: !!binary \"your_morty_proxy_key\"\n#   # [true|false] enable the \"proxy\" button next to each result\n#   proxify_results: true\n\n# communication with search engines\n#\noutgoing:\n  # default timeout in seconds, can be override by engine\n  request_timeout: 3.0\n  # the maximum timeout in seconds\n  # max_request_timeout: 10.0\n  # suffix of searx_useragent, could contain information like an email address\n  # to the administrator\n  useragent_suffix: \"\"\n  # The maximum number of concurrent connections that may be established.\n  pool_connections: 100\n  # Allow the connection pool to maintain keep-alive connections below this\n  # point.\n  pool_maxsize: 20\n  # See https://www.python-httpx.org/http2/\n  enable_http2: true\n  # uncomment below section if you want to use a custom server certificate\n  # see https://www.python-httpx.org/advanced/#changing-the-verification-defaults\n  # and https://www.python-httpx.org/compatibility/#ssl-configuration\n  #  verify: ~/.mitmproxy/mitmproxy-ca-cert.cer\n  #\n  # uncomment below section if you want to use a proxyq see: SOCKS proxies\n  #   https://2.python-requests.org/en/latest/user/advanced/#proxies\n  # are also supported: see\n  #   https://2.python-requests.org/en/latest/user/advanced/#socks\n  #\n  #  proxies:\n  #    all://:\n  #      - http://proxy1:8080\n  #      - http://proxy2:8080\n  #\n  #  using_tor_proxy: true\n  #\n  # Extra seconds to add in order to account for the time taken by the proxy\n  #\n  #  extra_proxy_timeout: 10\n  #\n  # uncomment below section only if you have more than one network interface\n  # which can be the source of outgoing search requests\n  #\n  #  source_ips:\n  #    - 1.1.1.1\n  #    - 1.1.1.2\n  #    - fe80::/126\n\n# External plugin configuration, for more details see\n#   https://docs.searxng.org/dev/plugins.html\n#\n# plugins:\n#   - plugin1\n#   - plugin2\n#   - ...\n\n# Comment or un-comment plugin to activate / deactivate by default.\n#\n# enabled_plugins:\n#   # these plugins are enabled if nothing is configured ..\n#   - 'Hash plugin'\n#   - 'Self Information'\n#   - 'Tracker URL remover'\n#   - 'Ahmia blacklist'  # activation depends on outgoing.using_tor_proxy\n#   # these plugins are disabled if nothing is configured ..\n#   - 'Hostname replace'  # see hostname_replace configuration below\n#   - 'Open Access DOI rewrite'\n#   - 'Tor check plugin'\n#   # Read the docs before activate: auto-detection of the language could be\n#   # detrimental to users expectations / users can activate the plugin in the\n#   # preferences if they want.\n#   - 'Autodetect search language'\n\n# Configuration of the \"Hostname replace\" plugin:\n#\n# hostname_replace:\n#   '(.*\\.)?youtube\\.com$': 'invidious.example.com'\n#   '(.*\\.)?youtu\\.be$': 'invidious.example.com'\n#   '(.*\\.)?youtube-noocookie\\.com$': 'yotter.example.com'\n#   '(.*\\.)?reddit\\.com$': 'teddit.example.com'\n#   '(.*\\.)?redd\\.it$': 'teddit.example.com'\n#   '(www\\.)?twitter\\.com$': 'nitter.example.com'\n#   # to remove matching host names from result list, set value to false\n#   'spam\\.example\\.com': false\n\nchecker:\n  # disable checker when in debug mode\n  off_when_debug: true\n\n  # use \"scheduling: false\" to disable scheduling\n  # scheduling: interval or int\n\n  # to activate the scheduler:\n  # * uncomment \"scheduling\" section\n  # * add \"cache2 = name=searxngcache,items=2000,blocks=2000,blocksize=4096,bitmap=1\"\n  #   to your uwsgi.ini\n\n  # scheduling:\n  #   start_after: [300, 1800]  # delay to start the first run of the checker\n  #   every: [86400, 90000]     # how often the checker runs\n\n  # additional tests: only for the YAML anchors (see the engines section)\n  #\n  additional_tests:\n    rosebud: &test_rosebud\n      matrix:\n        query: rosebud\n        lang: en\n      result_container:\n        - not_empty\n        - ['one_title_contains', 'citizen kane']\n      test:\n        - unique_results\n\n    android: &test_android\n      matrix:\n        query: ['android']\n        lang: ['en', 'de', 'fr', 'zh-CN']\n      result_container:\n        - not_empty\n        - ['one_title_contains', 'google']\n      test:\n        - unique_results\n\n  # tests: only for the YAML anchors (see the engines section)\n  tests:\n    infobox: &tests_infobox\n      infobox:\n        matrix:\n          query: [\"linux\", \"new york\", \"bbc\"]\n        result_container:\n          - has_infobox\n\ncategories_as_tabs:\n  general:\n  images:\n  videos:\n  news:\n  map:\n  music:\n  it:\n  science:\n  files:\n  social media:\n\nengines:\n  - name: 9gag\n    engine: 9gag\n    shortcut: 9g\n    disabled: true\n\n  - name: annas archive\n    engine: annas_archive\n    disabled: true\n    shortcut: aa\n\n  # - name: annas articles\n  #   engine: annas_archive\n  #   shortcut: aaa\n  #   # https://docs.searxng.org/dev/engines/online/annas_archive.html\n  #   aa_content: 'magazine' # book_fiction, book_unknown, book_nonfiction, book_comic\n  #   aa_ext: 'pdf'  # pdf, epub, ..\n  #   aa_sort: oldest'  # newest, oldest, largest, smallest\n\n  - name: apk mirror\n    engine: apkmirror\n    timeout: 4.0\n    shortcut: apkm\n    disabled: true\n\n  - name: apple app store\n    engine: apple_app_store\n    shortcut: aps\n    disabled: true\n\n  # Requires Tor\n  - name: ahmia\n    engine: ahmia\n    categories: onions\n    enable_http: true\n    shortcut: ah\n\n  - name: anaconda\n    engine: xpath\n    paging: true\n    first_page_num: 0\n    search_url: https://anaconda.org/search?q={query}&page={pageno}\n    results_xpath: //tbody/tr\n    url_xpath: ./td/h5/a[last()]/@href\n    title_xpath: ./td/h5\n    content_xpath: ./td[h5]/text()\n    categories: it\n    timeout: 6.0\n    shortcut: conda\n    disabled: true\n\n  - name: arch linux wiki\n    engine: archlinux\n    shortcut: al\n\n  - name: artic\n    engine: artic\n    shortcut: arc\n    timeout: 4.0\n\n  - name: arxiv\n    engine: arxiv\n    shortcut: arx\n    timeout: 4.0\n\n  - name: ask\n    engine: ask\n    shortcut: ask\n    disabled: true\n\n  # tmp suspended:  dh key too small\n  # - name: base\n  #   engine: base\n  #   shortcut: bs\n\n  - name: bandcamp\n    engine: bandcamp\n    shortcut: bc\n    categories: music\n\n  - name: wikipedia\n    engine: wikipedia\n    shortcut: wp\n    # add \"list\" to the array to get results in the results list\n    display_type: [\"infobox\"]\n    base_url: 'https://{language}.wikipedia.org/'\n    categories: [general]\n\n  - name: bilibili\n    engine: bilibili\n    shortcut: bil\n    disabled: true\n\n  - name: bing\n    engine: bing\n    shortcut: bi\n    disabled: true\n\n  - name: bing images\n    engine: bing_images\n    shortcut: bii\n\n  - name: bing news\n    engine: bing_news\n    shortcut: bin\n\n  - name: bing videos\n    engine: bing_videos\n    shortcut: biv\n\n  - name: bitbucket\n    engine: xpath\n    paging: true\n    search_url: https://bitbucket.org/repo/all/{pageno}?name={query}\n    url_xpath: //article[@class=\"repo-summary\"]//a[@class=\"repo-link\"]/@href\n    title_xpath: //article[@class=\"repo-summary\"]//a[@class=\"repo-link\"]\n    content_xpath: //article[@class=\"repo-summary\"]/p\n    categories: [it, repos]\n    timeout: 4.0\n    disabled: true\n    shortcut: bb\n    about:\n      website: https://bitbucket.org/\n      wikidata_id: Q2493781\n      official_api_documentation: https://developer.atlassian.com/bitbucket\n      use_official_api: false\n      require_api_key: false\n      results: HTML\n\n  - name: bpb\n    engine: bpb\n    shortcut: bpb\n    disabled: true\n\n  - name: btdigg\n    engine: btdigg\n    shortcut: bt\n    disabled: true\n\n  - name: ccc-tv\n    engine: xpath\n    paging: false\n    search_url: https://media.ccc.de/search/?q={query}\n    url_xpath: //div[@class=\"caption\"]/h3/a/@href\n    title_xpath: //div[@class=\"caption\"]/h3/a/text()\n    content_xpath: //div[@class=\"caption\"]/h4/@title\n    categories: videos\n    disabled: true\n    shortcut: c3tv\n    about:\n      website: https://media.ccc.de/\n      wikidata_id: Q80729951\n      official_api_documentation: https://github.com/voc/voctoweb\n      use_official_api: false\n      require_api_key: false\n      results: HTML\n      # We don't set language: de here because media.ccc.de is not just\n      # for a German audience. It contains many English videos and many\n      # German videos have English subtitles.\n\n  - name: openverse\n    engine: openverse\n    categories: images\n    shortcut: opv\n\n  - name: chefkoch\n    engine: chefkoch\n    shortcut: chef\n    # to show premium or plus results too:\n    # skip_premium: false\n\n  # - name: core.ac.uk\n  #   engine: core\n  #   categories: science\n  #   shortcut: cor\n  #   # get your API key from: https://core.ac.uk/api-keys/register/\n  #   api_key: 'unset'\n\n  - name: cppreference\n    engine: cppreference\n    shortcut: cpp\n    paging: false\n    disabled: true\n\n  - name: crossref\n    engine: crossref\n    shortcut: cr\n    timeout: 30\n    disabled: true\n\n  - name: crowdview\n    engine: json_engine\n    shortcut: cv\n    categories: general\n    paging: false\n    search_url: https://crowdview-next-js.onrender.com/api/search-v3?query={query}\n    results_query: results\n    url_query: link\n    title_query: title\n    content_query: snippet\n    disabled: true\n    about:\n      website: https://crowdview.ai/\n\n  - name: yep\n    engine: yep\n    shortcut: yep\n    categories: general\n    search_type: web\n    disabled: true\n\n  - name: yep images\n    engine: yep\n    shortcut: yepi\n    categories: images\n    search_type: images\n    disabled: true\n\n  - name: yep news\n    engine: yep\n    shortcut: yepn\n    categories: news\n    search_type: news\n    disabled: true\n\n  - name: curlie\n    engine: xpath\n    shortcut: cl\n    categories: general\n    disabled: true\n    paging: true\n    lang_all: ''\n    search_url: https://curlie.org/search?q={query}&lang={lang}&start={pageno}&stime=92452189\n    page_size: 20\n    results_xpath: //div[@id=\"site-list-content\"]/div[@class=\"site-item\"]\n    url_xpath: ./div[@class=\"title-and-desc\"]/a/@href\n    title_xpath: ./div[@class=\"title-and-desc\"]/a/div\n    content_xpath: ./div[@class=\"title-and-desc\"]/div[@class=\"site-descr\"]\n    about:\n      website: https://curlie.org/\n      wikidata_id: Q60715723\n      use_official_api: false\n      require_api_key: false\n      results: HTML\n\n  - name: currency\n    engine: currency_convert\n    categories: general\n    shortcut: cc\n\n  - name: bahnhof\n    engine: json_engine\n    search_url: https://www.bahnhof.de/api/stations/search/{query}\n    url_prefix: https://www.bahnhof.de/\n    url_query: slug\n    title_query: name\n    content_query: state\n    shortcut: bf\n    disabled: true\n    about:\n      website: https://www.bahn.de\n      wikidata_id: Q22811603\n      use_official_api: false\n      require_api_key: false\n      results: JSON\n      language: de\n    tests:\n      bahnhof:\n        matrix:\n          query: berlin\n          lang: en\n        result_container:\n          - not_empty\n          - ['one_title_contains', 'Berlin Hauptbahnhof']\n        test:\n          - unique_results\n\n  - name: deezer\n    engine: deezer\n    shortcut: dz\n    disabled: true\n\n  - name: destatis\n    engine: destatis\n    shortcut: destat\n    disabled: true\n\n  - name: deviantart\n    engine: deviantart\n    shortcut: da\n    timeout: 3.0\n\n  - name: ddg definitions\n    engine: duckduckgo_definitions\n    shortcut: ddd\n    weight: 2\n    disabled: true\n    tests: *tests_infobox\n\n  # cloudflare protected\n  # - name: digbt\n  #   engine: digbt\n  #   shortcut: dbt\n  #   timeout: 6.0\n  #   disabled: true\n\n  - name: docker hub\n    engine: docker_hub\n    shortcut: dh\n    categories: [it, packages]\n\n  - name: erowid\n    engine: xpath\n    paging: true\n    first_page_num: 0\n    page_size: 30\n    search_url: https://www.erowid.org/search.php?q={query}&s={pageno}\n    url_xpath: //dl[@class=\"results-list\"]/dt[@class=\"result-title\"]/a/@href\n    title_xpath: //dl[@class=\"results-list\"]/dt[@class=\"result-title\"]/a/text()\n    content_xpath: //dl[@class=\"results-list\"]/dd[@class=\"result-details\"]\n    categories: []\n    shortcut: ew\n    disabled: true\n    about:\n      website: https://www.erowid.org/\n      wikidata_id: Q1430691\n      official_api_documentation:\n      use_official_api: false\n      require_api_key: false\n      results: HTML\n\n  # - name: elasticsearch\n  #   shortcut: es\n  #   engine: elasticsearch\n  #   base_url: http://localhost:9200\n  #   username: elastic\n  #   password: changeme\n  #   index: my-index\n  #   # available options: match, simple_query_string, term, terms, custom\n  #   query_type: match\n  #   # if query_type is set to custom, provide your query here\n  #   #custom_query_json: {\"query\":{\"match_all\": {}}}\n  #   #show_metadata: false\n  #   disabled: true\n\n  - name: wikidata\n    engine: wikidata\n    shortcut: wd\n    timeout: 3.0\n    weight: 2\n    # add \"list\" to the array to get results in the results list\n    display_type: [\"infobox\"]\n    tests: *tests_infobox\n    categories: [general]\n\n  - name: duckduckgo\n    engine: duckduckgo\n    shortcut: ddg\n\n  - name: duckduckgo images\n    engine: duckduckgo_extra\n    categories: [images, web]\n    ddg_category: images\n    shortcut: ddi\n    disabled: true\n\n  - name: duckduckgo videos\n    engine: duckduckgo_extra\n    categories: [videos, web]\n    ddg_category: videos\n    shortcut: ddv\n    disabled: true\n\n  - name: duckduckgo news\n    engine: duckduckgo_extra\n    categories: [news, web]\n    ddg_category: news\n    shortcut: ddn\n    disabled: true\n\n  - name: duckduckgo weather\n    engine: duckduckgo_weather\n    shortcut: ddw\n    disabled: true\n\n  - name: apple maps\n    engine: apple_maps\n    shortcut: apm\n    disabled: true\n    timeout: 5.0\n\n  - name: emojipedia\n    engine: emojipedia\n    timeout: 4.0\n    shortcut: em\n    disabled: true\n\n  - name: tineye\n    engine: tineye\n    shortcut: tin\n    timeout: 9.0\n    disabled: true\n\n  - name: etymonline\n    engine: xpath\n    paging: true\n    search_url: https://etymonline.com/search?page={pageno}&q={query}\n    url_xpath: //a[contains(@class, \"word__name--\")]/@href\n    title_xpath: //a[contains(@class, \"word__name--\")]\n    content_xpath: //section[contains(@class, \"word__defination\")]\n    first_page_num: 1\n    shortcut: et\n    categories: [dictionaries]\n    about:\n      website: https://www.etymonline.com/\n      wikidata_id: Q1188617\n      official_api_documentation:\n      use_official_api: false\n      require_api_key: false\n      results: HTML\n\n  # - name: ebay\n  #   engine: ebay\n  #   shortcut: eb\n  #   base_url: 'https://www.ebay.com'\n  #   disabled: true\n  #   timeout: 5\n\n  - name: 1x\n    engine: www1x\n    shortcut: 1x\n    timeout: 3.0\n    disabled: true\n\n  - name: fdroid\n    engine: fdroid\n    shortcut: fd\n    disabled: true\n\n  - name: flickr\n    categories: images\n    shortcut: fl\n    # You can use the engine using the official stable API, but you need an API\n    # key, see: https://www.flickr.com/services/apps/create/\n    # engine: flickr\n    # api_key: 'apikey' # required!\n    # Or you can use the html non-stable engine, activated by default\n    engine: flickr_noapi\n\n  - name: free software directory\n    engine: mediawiki\n    shortcut: fsd\n    categories: [it, software wikis]\n    base_url: https://directory.fsf.org/\n    search_type: title\n    timeout: 5.0\n    disabled: true\n    about:\n      website: https://directory.fsf.org/\n      wikidata_id: Q2470288\n\n  # - name: freesound\n  #   engine: freesound\n  #   shortcut: fnd\n  #   disabled: true\n  #   timeout: 15.0\n  # API key required, see: https://freesound.org/docs/api/overview.html\n  #   api_key: MyAPIkey\n\n  - name: frinkiac\n    engine: frinkiac\n    shortcut: frk\n    disabled: true\n\n  - name: fyyd\n    engine: fyyd\n    shortcut: fy\n    timeout: 8.0\n    disabled: true\n\n  - name: genius\n    engine: genius\n    shortcut: gen\n\n  - name: gentoo\n    engine: gentoo\n    shortcut: ge\n    timeout: 10.0\n\n  - name: gitlab\n    engine: json_engine\n    paging: true\n    search_url: https://gitlab.com/api/v4/projects?search={query}&page={pageno}\n    url_query: web_url\n    title_query: name_with_namespace\n    content_query: description\n    page_size: 20\n    categories: [it, repos]\n    shortcut: gl\n    timeout: 10.0\n    disabled: true\n    about:\n      website: https://about.gitlab.com/\n      wikidata_id: Q16639197\n      official_api_documentation: https://docs.gitlab.com/ee/api/\n      use_official_api: false\n      require_api_key: false\n      results: JSON\n\n  - name: github\n    engine: github\n    shortcut: gh\n\n    # This a Gitea service. If you would like to use a different instance,\n    # change codeberg.org to URL of the desired Gitea host. Or you can create a\n    # new engine by copying this and changing the name, shortcut and search_url.\n\n  - name: codeberg\n    engine: json_engine\n    search_url: https://codeberg.org/api/v1/repos/search?q={query}&limit=10\n    url_query: html_url\n    title_query: name\n    content_query: description\n    categories: [it, repos]\n    shortcut: cb\n    disabled: true\n    about:\n      website: https://codeberg.org/\n      wikidata_id:\n      official_api_documentation: https://try.gitea.io/api/swagger\n      use_official_api: false\n      require_api_key: false\n      results: JSON\n\n  - name: goodreads\n    engine: goodreads\n    shortcut: good\n    timeout: 4.0\n    disabled: true\n\n  - name: google\n    engine: google\n    shortcut: go\n    # additional_tests:\n    #   android: *test_android\n\n  - name: google images\n    engine: google_images\n    shortcut: goi\n    # additional_tests:\n    #   android: *test_android\n    #   dali:\n    #     matrix:\n    #       query: ['Dali Christ']\n    #       lang: ['en', 'de', 'fr', 'zh-CN']\n    #     result_container:\n    #       - ['one_title_contains', 'Salvador']\n\n  - name: google news\n    engine: google_news\n    shortcut: gon\n    # additional_tests:\n    #   android: *test_android\n\n  - name: google videos\n    engine: google_videos\n    shortcut: gov\n    # additional_tests:\n    #   android: *test_android\n\n  - name: google scholar\n    engine: google_scholar\n    shortcut: gos\n\n  - name: google play apps\n    engine: google_play\n    categories: [files, apps]\n    shortcut: gpa\n    play_categ: apps\n    disabled: true\n\n  - name: google play movies\n    engine: google_play\n    categories: videos\n    shortcut: gpm\n    play_categ: movies\n    disabled: true\n\n  - name: material icons\n    engine: material_icons\n    categories: images\n    shortcut: mi\n    disabled: true\n\n  - name: gpodder\n    engine: json_engine\n    shortcut: gpod\n    timeout: 4.0\n    paging: false\n    search_url: https://gpodder.net/search.json?q={query}\n    url_query: url\n    title_query: title\n    content_query: description\n    page_size: 19\n    categories: music\n    disabled: true\n    about:\n      website: https://gpodder.net\n      wikidata_id: Q3093354\n      official_api_documentation: https://gpoddernet.readthedocs.io/en/latest/api/\n      use_official_api: false\n      requires_api_key: false\n      results: JSON\n\n  - name: habrahabr\n    engine: xpath\n    paging: true\n    search_url: https://habr.com/en/search/page{pageno}/?q={query}\n    results_xpath: //article[contains(@class, \"tm-articles-list__item\")]\n    url_xpath: .//a[@class=\"tm-title__link\"]/@href\n    title_xpath: .//a[@class=\"tm-title__link\"]\n    content_xpath: .//div[contains(@class, \"article-formatted-body\")]\n    categories: it\n    timeout: 4.0\n    disabled: true\n    shortcut: habr\n    about:\n      website: https://habr.com/\n      wikidata_id: Q4494434\n      official_api_documentation: https://habr.com/en/docs/help/api/\n      use_official_api: false\n      require_api_key: false\n      results: HTML\n\n  - name: hackernews\n    engine: hackernews\n    shortcut: hn\n    disabled: true\n\n  - name: hoogle\n    engine: xpath\n    search_url: https://hoogle.haskell.org/?hoogle={query}\n    results_xpath: '//div[@class=\"result\"]'\n    title_xpath: './/div[@class=\"ans\"]//a'\n    url_xpath: './/div[@class=\"ans\"]//a/@href'\n    content_xpath: './/div[@class=\"from\"]'\n    page_size: 20\n    categories: [it, packages]\n    shortcut: ho\n    about:\n      website: https://hoogle.haskell.org/\n      wikidata_id: Q34010\n      official_api_documentation: https://hackage.haskell.org/api\n      use_official_api: false\n      require_api_key: false\n      results: JSON\n\n  - name: imdb\n    engine: imdb\n    shortcut: imdb\n    timeout: 6.0\n    disabled: true\n\n  - name: imgur\n    engine: imgur\n    shortcut: img\n    disabled: true\n\n  - name: ina\n    engine: ina\n    shortcut: in\n    timeout: 6.0\n    disabled: true\n\n  - name: invidious\n    engine: invidious\n    # Instanes will be selected randomly, see https://api.invidious.io/ for\n    # instances that are stable (good uptime) and close to you.\n    base_url:\n      - https://invidious.io.lol\n      - https://invidious.fdn.fr\n      - https://yt.artemislena.eu\n      - https://invidious.tiekoetter.com\n      - https://invidious.flokinet.to\n      - https://vid.puffyan.us\n      - https://invidious.privacydev.net\n      - https://inv.tux.pizza\n    shortcut: iv\n    timeout: 3.0\n    disabled: true\n\n  - name: jisho\n    engine: jisho\n    shortcut: js\n    timeout: 3.0\n    disabled: true\n\n  - name: kickass\n    engine: kickass\n    base_url:\n      - https://kickasstorrents.to\n      - https://kickasstorrents.cr\n      - https://kickasstorrent.cr\n      - https://kickass.sx\n      - https://kat.am\n    shortcut: kc\n    timeout: 4.0\n\n  - name: lemmy communities\n    engine: lemmy\n    lemmy_type: Communities\n    shortcut: leco\n\n  - name: lemmy users\n    engine: lemmy\n    network: lemmy communities\n    lemmy_type: Users\n    shortcut: leus\n\n  - name: lemmy posts\n    engine: lemmy\n    network: lemmy communities\n    lemmy_type: Posts\n    shortcut: lepo\n\n  - name: lemmy comments\n    engine: lemmy\n    network: lemmy communities\n    lemmy_type: Comments\n    shortcut: lecom\n\n  - name: library genesis\n    engine: xpath\n    # search_url: https://libgen.is/search.php?req={query}\n    search_url: https://libgen.rs/search.php?req={query}\n    url_xpath: //a[contains(@href,\"book/index.php?md5\")]/@href\n    title_xpath: //a[contains(@href,\"book/\")]/text()[1]\n    content_xpath: //td/a[1][contains(@href,\"=author\")]/text()\n    categories: files\n    timeout: 7.0\n    disabled: true\n    shortcut: lg\n    about:\n      website: https://libgen.fun/\n      wikidata_id: Q22017206\n      official_api_documentation:\n      use_official_api: false\n      require_api_key: false\n      results: HTML\n\n  - name: z-library\n    engine: zlibrary\n    shortcut: zlib\n    categories: files\n    timeout: 7.0\n\n  - name: library of congress\n    engine: loc\n    shortcut: loc\n    categories: images\n\n  - name: lingva\n    engine: lingva\n    shortcut: lv\n    # set lingva instance in url, by default it will use the official instance\n    # url: https://lingva.thedaviddelta.com\n\n  - name: lobste.rs\n    engine: xpath\n    search_url: https://lobste.rs/search?q={query}&what=stories&order=relevance\n    results_xpath: //li[contains(@class, \"story\")]\n    url_xpath: .//a[@class=\"u-url\"]/@href\n    title_xpath: .//a[@class=\"u-url\"]\n    content_xpath: .//a[@class=\"domain\"]\n    categories: it\n    shortcut: lo\n    timeout: 5.0\n    disabled: true\n    about:\n      website: https://lobste.rs/\n      wikidata_id: Q60762874\n      official_api_documentation:\n      use_official_api: false\n      require_api_key: false\n      results: HTML\n\n  - name: azlyrics\n    shortcut: lyrics\n    engine: xpath\n    timeout: 4.0\n    disabled: true\n    categories: [music, lyrics]\n    paging: true\n    search_url: https://search.azlyrics.com/search.php?q={query}&w=lyrics&p={pageno}\n    url_xpath: //td[@class=\"text-left visitedlyr\"]/a/@href\n    title_xpath: //span/b/text()\n    content_xpath: //td[@class=\"text-left visitedlyr\"]/a/small\n    about:\n      website: https://azlyrics.com\n      wikidata_id: Q66372542\n      official_api_documentation:\n      use_official_api: false\n      require_api_key: false\n      results: HTML\n\n  - name: mastodon users\n    engine: mastodon\n    mastodon_type: accounts\n    base_url: https://mastodon.social\n    shortcut: mau\n\n  - name: mastodon hashtags\n    engine: mastodon\n    mastodon_type: hashtags\n    base_url: https://mastodon.social\n    shortcut: mah\n\n  # - name: matrixrooms\n  #   engine: mrs\n  #   # https://docs.searxng.org/dev/engines/online/mrs.html\n  #   # base_url: https://mrs-api-host\n  #   shortcut: mtrx\n  #   disabled: true\n\n  - name: mdn\n    shortcut: mdn\n    engine: json_engine\n    categories: [it]\n    paging: true\n    search_url: https://developer.mozilla.org/api/v1/search?q={query}&page={pageno}\n    results_query: documents\n    url_query: mdn_url\n    url_prefix: https://developer.mozilla.org\n    title_query: title\n    content_query: summary\n    about:\n      website: https://developer.mozilla.org\n      wikidata_id: Q3273508\n      official_api_documentation: null\n      use_official_api: false\n      require_api_key: false\n      results: JSON\n\n  - name: metacpan\n    engine: metacpan\n    shortcut: cpan\n    disabled: true\n    number_of_results: 20\n\n  # - name: meilisearch\n  #   engine: meilisearch\n  #   shortcut: mes\n  #   enable_http: true\n  #   base_url: http://localhost:7700\n  #   index: my-index\n\n  - name: mixcloud\n    engine: mixcloud\n    shortcut: mc\n\n  # MongoDB engine\n  # Required dependency: pymongo\n  # - name: mymongo\n  #   engine: mongodb\n  #   shortcut: md\n  #   exact_match_only: false\n  #   host: '127.0.0.1'\n  #   port: 27017\n  #   enable_http: true\n  #   results_per_page: 20\n  #   database: 'business'\n  #   collection: 'reviews'  # name of the db collection\n  #   key: 'name'  # key in the collection to search for\n\n  - name: mozhi\n    engine: mozhi\n    base_url:\n      - https://mozhi.aryak.me\n      - https://translate.bus-hit.me\n      - https://nyc1.mz.ggtyler.dev\n    # mozhi_engine: google - see https://mozhi.aryak.me for supported engines\n    timeout: 4.0\n    shortcut: mz\n    disabled: true\n\n  - name: mwmbl\n    engine: mwmbl\n    # api_url: https://api.mwmbl.org\n    shortcut: mwm\n    disabled: true\n\n  - name: npm\n    engine: npm\n    shortcut: npm\n    timeout: 5.0\n    disabled: true\n\n  - name: nyaa\n    engine: nyaa\n    shortcut: nt\n    disabled: true\n\n  - name: mankier\n    engine: json_engine\n    search_url: https://www.mankier.com/api/v2/mans/?q={query}\n    results_query: results\n    url_query: url\n    title_query: name\n    content_query: description\n    categories: it\n    shortcut: man\n    about:\n      website: https://www.mankier.com/\n      official_api_documentation: https://www.mankier.com/api\n      use_official_api: true\n      require_api_key: false\n      results: JSON\n\n  # read https://docs.searxng.org/dev/engines/online/mullvad_leta.html\n  # - name: mullvadleta\n  #   engine: mullvad_leta\n  #   use_cache: true  # Only 100 non-cache searches per day, suggested only for private instances\n  #   search_url: https://leta.mullvad.net\n  #   categories: [general, web]\n  #   shortcut: ml\n\n  - name: odysee\n    engine: odysee\n    shortcut: od\n    disabled: true\n\n  - name: openairedatasets\n    engine: json_engine\n    paging: true\n    search_url: https://api.openaire.eu/search/datasets?format=json&page={pageno}&size=10&title={query}\n    results_query: response/results/result\n    url_query: metadata/oaf:entity/oaf:result/children/instance/webresource/url/$\n    title_query: metadata/oaf:entity/oaf:result/title/$\n    content_query: metadata/oaf:entity/oaf:result/description/$\n    content_html_to_text: true\n    categories: \"science\"\n    shortcut: oad\n    timeout: 5.0\n    about:\n      website: https://www.openaire.eu/\n      wikidata_id: Q25106053\n      official_api_documentation: https://api.openaire.eu/\n      use_official_api: false\n      require_api_key: false\n      results: JSON\n\n  - name: openairepublications\n    engine: json_engine\n    paging: true\n    search_url: https://api.openaire.eu/search/publications?format=json&page={pageno}&size=10&title={query}\n    results_query: response/results/result\n    url_query: metadata/oaf:entity/oaf:result/children/instance/webresource/url/$\n    title_query: metadata/oaf:entity/oaf:result/title/$\n    content_query: metadata/oaf:entity/oaf:result/description/$\n    content_html_to_text: true\n    categories: science\n    shortcut: oap\n    timeout: 5.0\n    about:\n      website: https://www.openaire.eu/\n      wikidata_id: Q25106053\n      official_api_documentation: https://api.openaire.eu/\n      use_official_api: false\n      require_api_key: false\n      results: JSON\n\n  # - name: opensemanticsearch\n  #   engine: opensemantic\n  #   shortcut: oss\n  #   base_url: 'http://localhost:8983/solr/opensemanticsearch/'\n\n  - name: openstreetmap\n    engine: openstreetmap\n    shortcut: osm\n\n  - name: openrepos\n    engine: xpath\n    paging: true\n    search_url: https://openrepos.net/search/node/{query}?page={pageno}\n    url_xpath: //li[@class=\"search-result\"]//h3[@class=\"title\"]/a/@href\n    title_xpath: //li[@class=\"search-result\"]//h3[@class=\"title\"]/a\n    content_xpath: //li[@class=\"search-result\"]//div[@class=\"search-snippet-info\"]//p[@class=\"search-snippet\"]\n    categories: files\n    timeout: 4.0\n    disabled: true\n    shortcut: or\n    about:\n      website: https://openrepos.net/\n      wikidata_id:\n      official_api_documentation:\n      use_official_api: false\n      require_api_key: false\n      results: HTML\n\n  - name: packagist\n    engine: json_engine\n    paging: true\n    search_url: https://packagist.org/search.json?q={query}&page={pageno}\n    results_query: results\n    url_query: url\n    title_query: name\n    content_query: description\n    categories: [it, packages]\n    disabled: true\n    timeout: 5.0\n    shortcut: pack\n    about:\n      website: https://packagist.org\n      wikidata_id: Q108311377\n      official_api_documentation: https://packagist.org/apidoc\n      use_official_api: true\n      require_api_key: false\n      results: JSON\n\n  - name: pdbe\n    engine: pdbe\n    shortcut: pdb\n    # Hide obsolete PDB entries.  Default is not to hide obsolete structures\n    #  hide_obsolete: false\n\n  - name: photon\n    engine: photon\n    shortcut: ph\n\n  - name: pinterest\n    engine: pinterest\n    shortcut: pin\n\n  - name: piped\n    engine: piped\n    shortcut: ppd\n    categories: videos\n    piped_filter: videos\n    timeout: 3.0\n\n    # URL to use as link and for embeds\n    frontend_url: https://srv.piped.video\n    # Instance will be selected randomly, for more see https://piped-instances.kavin.rocks/\n    backend_url:\n      - https://pipedapi.kavin.rocks\n      - https://pipedapi-libre.kavin.rocks\n      - https://pipedapi.adminforge.de\n\n  - name: piped.music\n    engine: piped\n    network: piped\n    shortcut: ppdm\n    categories: music\n    piped_filter: music_songs\n    timeout: 3.0\n\n  - name: piratebay\n    engine: piratebay\n    shortcut: tpb\n    # You may need to change this URL to a proxy if piratebay is blocked in your\n    # country\n    url: https://thepiratebay.org/\n    timeout: 3.0\n\n  - name: pixiv\n    shortcut: pv\n    engine: pixiv\n    disabled: true\n    inactive: true\n    pixiv_image_proxies:\n      - pximg.example.org\n      # A proxy is required to load the images. Hosting an image proxy server\n      # for Pixiv:\n      #    --> https://codeberg.org/VnPower/PixivFE/wiki/Hosting-an-image-proxy-server-for-Pixiv\n      # Proxies from public instances.  Ask the public instances owners if they\n      # agree to receive traffic from SearXNG!\n      #    --> https://codeberg.org/VnPower/PixivFE#instances\n      #    --> https://github.com/searxng/searxng/pull/3192#issuecomment-1941095047\n      # image proxy of https://pixiv.cat\n      # - https://i.pixiv.cat\n      # image proxy of https://www.pixiv.pics\n      # - https://pximg.cocomi.eu.org\n      # image proxy of https://pixivfe.exozy.me\n      # - https://pximg.exozy.me\n      # image proxy of https://pixivfe.ducks.party\n      # - https://pixiv.ducks.party\n      # image proxy of https://pixiv.perennialte.ch\n      # - https://pximg.perennialte.ch\n\n  - name: podcastindex\n    engine: podcastindex\n    shortcut: podcast\n\n  # Required dependency: psychopg2\n  #  - name: postgresql\n  #    engine: postgresql\n  #    database: postgres\n  #    username: postgres\n  #    password: postgres\n  #    limit: 10\n  #    query_str: 'SELECT * from my_table WHERE my_column = %(query)s'\n  #    shortcut : psql\n\n  - name: presearch\n    engine: presearch\n    search_type: search\n    categories: [general, web]\n    shortcut: ps\n    timeout: 4.0\n    disabled: true\n\n  - name: presearch images\n    engine: presearch\n    network: presearch\n    search_type: images\n    categories: [images, web]\n    timeout: 4.0\n    shortcut: psimg\n    disabled: true\n\n  - name: presearch videos\n    engine: presearch\n    network: presearch\n    search_type: videos\n    categories: [general, web]\n    timeout: 4.0\n    shortcut: psvid\n    disabled: true\n\n  - name: presearch news\n    engine: presearch\n    network: presearch\n    search_type: news\n    categories: [news, web]\n    timeout: 4.0\n    shortcut: psnews\n    disabled: true\n\n  - name: pub.dev\n    engine: xpath\n    shortcut: pd\n    search_url: https://pub.dev/packages?q={query}&page={pageno}\n    paging: true\n    results_xpath: //div[contains(@class,\"packages-item\")]\n    url_xpath: ./div/h3/a/@href\n    title_xpath: ./div/h3/a\n    content_xpath: ./div/div/div[contains(@class,\"packages-description\")]/span\n    categories: [packages, it]\n    timeout: 3.0\n    disabled: true\n    first_page_num: 1\n    about:\n      website: https://pub.dev/\n      official_api_documentation: https://pub.dev/help/api\n      use_official_api: false\n      require_api_key: false\n      results: HTML\n\n  - name: pubmed\n    engine: pubmed\n    shortcut: pub\n    timeout: 3.0\n\n  - name: pypi\n    shortcut: pypi\n    engine: pypi\n\n  - name: qwant\n    qwant_categ: web\n    engine: qwant\n    shortcut: qw\n    categories: [general, web]\n    additional_tests:\n      rosebud: *test_rosebud\n\n  - name: qwant news\n    qwant_categ: news\n    engine: qwant\n    shortcut: qwn\n    categories: news\n    network: qwant\n\n  - name: qwant images\n    qwant_categ: images\n    engine: qwant\n    shortcut: qwi\n    categories: [images, web]\n    network: qwant\n\n  - name: qwant videos\n    qwant_categ: videos\n    engine: qwant\n    shortcut: qwv\n    categories: [videos, web]\n    network: qwant\n\n  # - name: library\n  #   engine: recoll\n  #   shortcut: lib\n  #   base_url: 'https://recoll.example.org/'\n  #   search_dir: ''\n  #   mount_prefix: /export\n  #   dl_prefix: 'https://download.example.org'\n  #   timeout: 30.0\n  #   categories: files\n  #   disabled: true\n\n  # - name: recoll library reference\n  #   engine: recoll\n  #   base_url: 'https://recoll.example.org/'\n  #   search_dir: reference\n  #   mount_prefix: /export\n  #   dl_prefix: 'https://download.example.org'\n  #   shortcut: libr\n  #   timeout: 30.0\n  #   categories: files\n  #   disabled: true\n\n  - name: radio browser\n    engine: radio_browser\n    shortcut: rb\n\n  - name: reddit\n    engine: reddit\n    shortcut: re\n    page_size: 25\n\n  - name: rottentomatoes\n    engine: rottentomatoes\n    shortcut: rt\n    disabled: true\n\n  # Required dependency: redis\n  # - name: myredis\n  #   shortcut : rds\n  #   engine: redis_server\n  #   exact_match_only: false\n  #   host: '127.0.0.1'\n  #   port: 6379\n  #   enable_http: true\n  #   password: ''\n  #   db: 0\n\n  # tmp suspended: bad certificate\n  #  - name: scanr structures\n  #    shortcut: scs\n  #    engine: scanr_structures\n  #    disabled: true\n\n  - name: sepiasearch\n    engine: sepiasearch\n    shortcut: sep\n\n  - name: soundcloud\n    engine: soundcloud\n    shortcut: sc\n\n  - name: stackoverflow\n    engine: stackexchange\n    shortcut: st\n    api_site: 'stackoverflow'\n    categories: [it, q&a]\n\n  - name: askubuntu\n    engine: stackexchange\n    shortcut: ubuntu\n    api_site: 'askubuntu'\n    categories: [it, q&a]\n\n  - name: internetarchivescholar\n    engine: internet_archive_scholar\n    shortcut: ias\n    timeout: 15.0\n\n  - name: superuser\n    engine: stackexchange\n    shortcut: su\n    api_site: 'superuser'\n    categories: [it, q&a]\n\n  - name: searchcode code\n    engine: searchcode_code\n    shortcut: scc\n    disabled: true\n\n  # - name: searx\n  #   engine: searx_engine\n  #   shortcut: se\n  #   instance_urls :\n  #       - http://127.0.0.1:8888/\n  #       - ...\n  #   disabled: true\n\n  - name: semantic scholar\n    engine: semantic_scholar\n    disabled: true\n    shortcut: se\n\n  # Spotify needs API credentials\n  # - name: spotify\n  #   engine: spotify\n  #   shortcut: stf\n  #   api_client_id: *******\n  #   api_client_secret: *******\n\n  # - name: solr\n  #   engine: solr\n  #   shortcut: slr\n  #   base_url: http://localhost:8983\n  #   collection: collection_name\n  #   sort: '' # sorting: asc or desc\n  #   field_list: '' # comma separated list of field names to display on the UI\n  #   default_fields: '' # default field to query\n  #   query_fields: '' # query fields\n  #   enable_http: true\n\n  # - name: springer nature\n  #   engine: springer\n  #   # get your API key from: https://dev.springernature.com/signup\n  #   # working API key, for test & debug: \"a69685087d07eca9f13db62f65b8f601\"\n  #   api_key: 'unset'\n  #   shortcut: springer\n  #   timeout: 15.0\n\n  - name: startpage\n    engine: startpage\n    shortcut: sp\n    timeout: 6.0\n    disabled: true\n    additional_tests:\n      rosebud: *test_rosebud\n\n  - name: tokyotoshokan\n    engine: tokyotoshokan\n    shortcut: tt\n    timeout: 6.0\n    disabled: true\n\n  - name: solidtorrents\n    engine: solidtorrents\n    shortcut: solid\n    timeout: 4.0\n    base_url:\n      - https://solidtorrents.to\n      - https://bitsearch.to\n\n  # For this demo of the sqlite engine download:\n  #   https://liste.mediathekview.de/filmliste-v2.db.bz2\n  # and unpack into searx/data/filmliste-v2.db\n  # Query to test: \"!demo concert\"\n  #\n  # - name: demo\n  #   engine: sqlite\n  #   shortcut: demo\n  #   categories: general\n  #   result_template: default.html\n  #   database: searx/data/filmliste-v2.db\n  #   query_str:  >-\n  #     SELECT title || ' (' || time(duration, 'unixepoch') || ')' AS title,\n  #            COALESCE( NULLIF(url_video_hd,''), NULLIF(url_video_sd,''), url_video) AS url,\n  #            description AS content\n  #       FROM film\n  #      WHERE title LIKE :wildcard OR description LIKE :wildcard\n  #      ORDER BY duration DESC\n\n  - name: tagesschau\n    engine: tagesschau\n    # when set to false, display URLs from Tagesschau, and not the actual source\n    # (e.g. NDR, WDR, SWR, HR, ...)\n    use_source_url: true\n    shortcut: ts\n    disabled: true\n\n  - name: tmdb\n    engine: xpath\n    paging: true\n    categories: movies\n    search_url: https://www.themoviedb.org/search?page={pageno}&query={query}\n    results_xpath: //div[contains(@class,\"movie\") or contains(@class,\"tv\")]//div[contains(@class,\"card\")]\n    url_xpath: .//div[contains(@class,\"poster\")]/a/@href\n    thumbnail_xpath: .//img/@src\n    title_xpath: .//div[contains(@class,\"title\")]//h2\n    content_xpath: .//div[contains(@class,\"overview\")]\n    shortcut: tm\n    disabled: true\n\n  # Requires Tor\n  - name: torch\n    engine: xpath\n    paging: true\n    search_url:\n      http://xmh57jrknzkhv6y3ls3ubitzfqnkrwxhopf5aygthi7d6rplyvk3noyd.onion/cgi-bin/omega/omega?P={query}&DEFAULTOP=and\n    results_xpath: //table//tr\n    url_xpath: ./td[2]/a\n    title_xpath: ./td[2]/b\n    content_xpath: ./td[2]/small\n    categories: onions\n    enable_http: true\n    shortcut: tch\n\n  # torznab engine lets you query any torznab compatible indexer.  Using this\n  # engine in combination with Jackett opens the possibility to query a lot of\n  # public and private indexers directly from SearXNG. More details at:\n  # https://docs.searxng.org/dev/engines/online/torznab.html\n  #\n  # - name: Torznab EZTV\n  #   engine: torznab\n  #   shortcut: eztv\n  #   base_url: http://localhost:9117/api/v2.0/indexers/eztv/results/torznab\n  #   enable_http: true  # if using localhost\n  #   api_key: xxxxxxxxxxxxxxx\n  #   show_magnet_links: true\n  #   show_torrent_files: false\n  #   # https://github.com/Jackett/Jackett/wiki/Jackett-Categories\n  #   torznab_categories:  # optional\n  #     - 2000\n  #     - 5000\n\n  # tmp suspended - too slow, too many errors\n  #  - name: urbandictionary\n  #    engine      : xpath\n  #    search_url  : https://www.urbandictionary.com/define.php?term={query}\n  #    url_xpath   : //*[@class=\"word\"]/@href\n  #    title_xpath : //*[@class=\"def-header\"]\n  #    content_xpath: //*[@class=\"meaning\"]\n  #    shortcut: ud\n\n  - name: unsplash\n    engine: unsplash\n    shortcut: us\n\n  - name: yandex music\n    engine: yandex_music\n    shortcut: ydm\n    disabled: true\n    # https://yandex.com/support/music/access.html\n    inactive: true\n\n  - name: yahoo\n    engine: yahoo\n    shortcut: yh\n    disabled: true\n\n  - name: yahoo news\n    engine: yahoo_news\n    shortcut: yhn\n\n  - name: youtube\n    shortcut: yt\n    # You can use the engine using the official stable API, but you need an API\n    # key See: https://console.developers.google.com/project\n    #\n    # engine: youtube_api\n    # api_key: 'apikey' # required!\n    #\n    # Or you can use the html non-stable engine, activated by default\n    engine: youtube_noapi\n\n  - name: dailymotion\n    engine: dailymotion\n    shortcut: dm\n\n  - name: vimeo\n    engine: vimeo\n    shortcut: vm\n\n  - name: wiby\n    engine: json_engine\n    paging: true\n    search_url: https://wiby.me/json/?q={query}&p={pageno}\n    url_query: URL\n    title_query: Title\n    content_query: Snippet\n    categories: [general, web]\n    shortcut: wib\n    disabled: true\n    about:\n      website: https://wiby.me/\n\n  - name: alexandria\n    engine: json_engine\n    shortcut: alx\n    categories: general\n    paging: true\n    search_url: https://api.alexandria.org/?a=1&q={query}&p={pageno}\n    results_query: results\n    title_query: title\n    url_query: url\n    content_query: snippet\n    timeout: 1.5\n    disabled: true\n    about:\n      website: https://alexandria.org/\n      official_api_documentation: https://github.com/alexandria-org/alexandria-api/raw/master/README.md\n      use_official_api: true\n      require_api_key: false\n      results: JSON\n\n  - name: wikibooks\n    engine: mediawiki\n    weight: 0.5\n    shortcut: wb\n    categories: [general, wikimedia]\n    base_url: \"https://{language}.wikibooks.org/\"\n    search_type: text\n    disabled: true\n    about:\n      website: https://www.wikibooks.org/\n      wikidata_id: Q367\n\n  - name: wikinews\n    engine: mediawiki\n    shortcut: wn\n    categories: [news, wikimedia]\n    base_url: \"https://{language}.wikinews.org/\"\n    search_type: text\n    srsort: create_timestamp_desc\n    about:\n      website: https://www.wikinews.org/\n      wikidata_id: Q964\n\n  - name: wikiquote\n    engine: mediawiki\n    weight: 0.5\n    shortcut: wq\n    categories: [general, wikimedia]\n    base_url: \"https://{language}.wikiquote.org/\"\n    search_type: text\n    disabled: true\n    additional_tests:\n      rosebud: *test_rosebud\n    about:\n      website: https://www.wikiquote.org/\n      wikidata_id: Q369\n\n  - name: wikisource\n    engine: mediawiki\n    weight: 0.5\n    shortcut: ws\n    categories: [general, wikimedia]\n    base_url: \"https://{language}.wikisource.org/\"\n    search_type: text\n    disabled: true\n    about:\n      website: https://www.wikisource.org/\n      wikidata_id: Q263\n\n  - name: wikispecies\n    engine: mediawiki\n    shortcut: wsp\n    categories: [general, science, wikimedia]\n    base_url: \"https://species.wikimedia.org/\"\n    search_type: text\n    disabled: true\n    about:\n      website: https://species.wikimedia.org/\n      wikidata_id: Q13679\n    tests:\n      wikispecies:\n        matrix:\n          query: \"Campbell, L.I. et al. 2011: MicroRNAs\"\n          lang: en\n        result_container:\n          - not_empty\n          - ['one_title_contains', 'Tardigrada']\n        test:\n          - unique_results\n\n  - name: wiktionary\n    engine: mediawiki\n    shortcut: wt\n    categories: [dictionaries, wikimedia]\n    base_url: \"https://{language}.wiktionary.org/\"\n    search_type: text\n    about:\n      website: https://www.wiktionary.org/\n      wikidata_id: Q151\n\n  - name: wikiversity\n    engine: mediawiki\n    weight: 0.5\n    shortcut: wv\n    categories: [general, wikimedia]\n    base_url: \"https://{language}.wikiversity.org/\"\n    search_type: text\n    disabled: true\n    about:\n      website: https://www.wikiversity.org/\n      wikidata_id: Q370\n\n  - name: wikivoyage\n    engine: mediawiki\n    weight: 0.5\n    shortcut: wy\n    categories: [general, wikimedia]\n    base_url: \"https://{language}.wikivoyage.org/\"\n    search_type: text\n    disabled: true\n    about:\n      website: https://www.wikivoyage.org/\n      wikidata_id: Q373\n\n  - name: wikicommons.images\n    engine: wikicommons\n    shortcut: wc\n    categories: images\n    number_of_results: 10\n\n  - name: wolframalpha\n    shortcut: wa\n    # You can use the engine using the official stable API, but you need an API\n    # key.  See: https://products.wolframalpha.com/api/\n    #\n    # engine: wolframalpha_api\n    # api_key: ''\n    #\n    # Or you can use the html non-stable engine, activated by default\n    engine: wolframalpha_noapi\n    timeout: 6.0\n    categories: general\n    disabled: true\n\n  - name: dictzone\n    engine: dictzone\n    shortcut: dc\n\n  - name: mymemory translated\n    engine: translated\n    shortcut: tl\n    timeout: 5.0\n    # You can use without an API key, but you are limited to 1000 words/day\n    # See: https://mymemory.translated.net/doc/usagelimits.php\n    # api_key: ''\n\n  # Required dependency: mysql-connector-python\n  #  - name: mysql\n  #    engine: mysql_server\n  #    database: mydatabase\n  #    username: user\n  #    password: pass\n  #    limit: 10\n  #    query_str: 'SELECT * from mytable WHERE fieldname=%(query)s'\n  #    shortcut: mysql\n\n  - name: 1337x\n    engine: 1337x\n    shortcut: 1337x\n    disabled: true\n\n  - name: duden\n    engine: duden\n    shortcut: du\n    disabled: true\n\n  - name: seznam\n    shortcut: szn\n    engine: seznam\n    disabled: true\n\n  # - name: deepl\n  #   engine: deepl\n  #   shortcut: dpl\n  #   # You can use the engine using the official stable API, but you need an API key\n  #   # See: https://www.deepl.com/pro-api?cta=header-pro-api\n  #   api_key: ''  # required!\n  #   timeout: 5.0\n  #   disabled: true\n\n  - name: mojeek\n    shortcut: mjk\n    engine: xpath\n    paging: true\n    categories: [general, web]\n    search_url: https://www.mojeek.com/search?q={query}&s={pageno}&lang={lang}&lb={lang}\n    results_xpath: //ul[@class=\"results-standard\"]/li/a[@class=\"ob\"]\n    url_xpath: ./@href\n    title_xpath: ../h2/a\n    content_xpath: ..//p[@class=\"s\"]\n    suggestion_xpath: //div[@class=\"top-info\"]/p[@class=\"top-info spell\"]/em/a\n    first_page_num: 0\n    page_size: 10\n    max_page: 100\n    disabled: true\n    about:\n      website: https://www.mojeek.com/\n      wikidata_id: Q60747299\n      official_api_documentation: https://www.mojeek.com/services/api.html/\n      use_official_api: false\n      require_api_key: false\n      results: HTML\n\n  - name: moviepilot\n    engine: moviepilot\n    shortcut: mp\n    disabled: true\n\n  - name: naver\n    shortcut: nvr\n    categories: [general, web]\n    engine: xpath\n    paging: true\n    search_url: https://search.naver.com/search.naver?where=webkr&sm=osp_hty&ie=UTF-8&query={query}&start={pageno}\n    url_xpath: //a[@class=\"link_tit\"]/@href\n    title_xpath: //a[@class=\"link_tit\"]\n    content_xpath: //div[@class=\"total_dsc_wrap\"]/a\n    first_page_num: 1\n    page_size: 10\n    disabled: true\n    about:\n      website: https://www.naver.com/\n      wikidata_id: Q485639\n      official_api_documentation: https://developers.naver.com/docs/nmt/examples/\n      use_official_api: false\n      require_api_key: false\n      results: HTML\n      language: ko\n\n  - name: rubygems\n    shortcut: rbg\n    engine: xpath\n    paging: true\n    search_url: https://rubygems.org/search?page={pageno}&query={query}\n    results_xpath: /html/body/main/div/a[@class=\"gems__gem\"]\n    url_xpath: ./@href\n    title_xpath: ./span/h2\n    content_xpath: ./span/p\n    suggestion_xpath: /html/body/main/div/div[@class=\"search__suggestions\"]/p/a\n    first_page_num: 1\n    categories: [it, packages]\n    disabled: true\n    about:\n      website: https://rubygems.org/\n      wikidata_id: Q1853420\n      official_api_documentation: https://guides.rubygems.org/rubygems-org-api/\n      use_official_api: false\n      require_api_key: false\n      results: HTML\n\n  - name: peertube\n    engine: peertube\n    shortcut: ptb\n    paging: true\n    # alternatives see: https://instances.joinpeertube.org/instances\n    # base_url: https://tube.4aem.com\n    categories: videos\n    disabled: true\n    timeout: 6.0\n\n  - name: mediathekviewweb\n    engine: mediathekviewweb\n    shortcut: mvw\n    disabled: true\n\n  - name: yacy\n    engine: yacy\n    categories: general\n    search_type: text\n    base_url: https://yacy.searchlab.eu\n    shortcut: ya\n    disabled: true\n    # required if you aren't using HTTPS for your local yacy instance\n    # https://docs.searxng.org/dev/engines/online/yacy.html\n    # enable_http: true\n    # timeout: 3.0\n    # search_mode: 'global'\n\n  - name: yacy images\n    engine: yacy\n    categories: images\n    search_type: image\n    base_url: https://yacy.searchlab.eu\n    shortcut: yai\n    disabled: true\n\n  - name: rumble\n    engine: rumble\n    shortcut: ru\n    base_url: https://rumble.com/\n    paging: true\n    categories: videos\n    disabled: true\n\n  - name: livespace\n    engine: livespace\n    shortcut: ls\n    categories: videos\n    disabled: true\n    timeout: 5.0\n\n  - name: wordnik\n    engine: wordnik\n    shortcut: def\n    base_url: https://www.wordnik.com/\n    categories: [dictionaries]\n    timeout: 5.0\n\n  - name: woxikon.de synonyme\n    engine: xpath\n    shortcut: woxi\n    categories: [dictionaries]\n    timeout: 5.0\n    disabled: true\n    search_url: https://synonyme.woxikon.de/synonyme/{query}.php\n    url_xpath: //div[@class=\"upper-synonyms\"]/a/@href\n    content_xpath: //div[@class=\"synonyms-list-group\"]\n    title_xpath: //div[@class=\"upper-synonyms\"]/a\n    no_result_for_http_status: [404]\n    about:\n      website: https://www.woxikon.de/\n      wikidata_id:  # No Wikidata ID\n      use_official_api: false\n      require_api_key: false\n      results: HTML\n      language: de\n\n  - name: seekr news\n    engine: seekr\n    shortcut: senews\n    categories: news\n    seekr_category: news\n    disabled: true\n\n  - name: seekr images\n    engine: seekr\n    network: seekr news\n    shortcut: seimg\n    categories: images\n    seekr_category: images\n    disabled: true\n\n  - name: seekr videos\n    engine: seekr\n    network: seekr news\n    shortcut: sevid\n    categories: videos\n    seekr_category: videos\n    disabled: true\n\n  - name: sjp.pwn\n    engine: sjp\n    shortcut: sjp\n    base_url: https://sjp.pwn.pl/\n    timeout: 5.0\n    disabled: true\n\n  - name: stract\n    engine: stract\n    shortcut: str\n    disabled: true\n\n  - name: svgrepo\n    engine: svgrepo\n    shortcut: svg\n    timeout: 10.0\n    disabled: true\n\n  - name: tootfinder\n    engine: tootfinder\n    shortcut: toot\n\n  - name: voidlinux\n    engine: voidlinux\n    shortcut: void\n    disabled: true\n\n  - name: wallhaven\n    engine: wallhaven\n    # api_key: abcdefghijklmnopqrstuvwxyz\n    shortcut: wh\n\n    # wikimini: online encyclopedia for children\n    # The fulltext and title parameter is necessary for Wikimini because\n    # sometimes it will not show the results and redirect instead\n  - name: wikimini\n    engine: xpath\n    shortcut: wkmn\n    search_url: https://fr.wikimini.org/w/index.php?search={query}&title=Sp%C3%A9cial%3ASearch&fulltext=Search\n    url_xpath: //li/div[@class=\"mw-search-result-heading\"]/a/@href\n    title_xpath: //li//div[@class=\"mw-search-result-heading\"]/a\n    content_xpath: //li/div[@class=\"searchresult\"]\n    categories: general\n    disabled: true\n    about:\n      website: https://wikimini.org/\n      wikidata_id: Q3568032\n      use_official_api: false\n      require_api_key: false\n      results: HTML\n      language: fr\n\n  - name: wttr.in\n    engine: wttr\n    shortcut: wttr\n    timeout: 9.0\n\n  - name: yummly\n    engine: yummly\n    shortcut: yum\n    disabled: true\n\n  - name: brave\n    engine: brave\n    shortcut: br\n    time_range_support: true\n    paging: true\n    categories: [general, web]\n    brave_category: search\n    # brave_spellcheck: true\n\n  - name: brave.images\n    engine: brave\n    network: brave\n    shortcut: brimg\n    categories: [images, web]\n    brave_category: images\n\n  - name: brave.videos\n    engine: brave\n    network: brave\n    shortcut: brvid\n    categories: [videos, web]\n    brave_category: videos\n\n  - name: brave.news\n    engine: brave\n    network: brave\n    shortcut: brnews\n    categories: news\n    brave_category: news\n\n  # - name: brave.goggles\n  #   engine: brave\n  #   network: brave\n  #   shortcut: brgog\n  #   time_range_support: true\n  #   paging: true\n  #   categories: [general, web]\n  #   brave_category: goggles\n  #   Goggles: # required! This should be a URL ending in .goggle\n\n  - name: lib.rs\n    shortcut: lrs\n    engine: lib_rs\n    disabled: true\n\n  - name: sourcehut\n    shortcut: srht\n    engine: xpath\n    paging: true\n    search_url: https://sr.ht/projects?page={pageno}&search={query}\n    results_xpath: (//div[@class=\"event-list\"])[1]/div[@class=\"event\"]\n    url_xpath: ./h4/a[2]/@href\n    title_xpath: ./h4/a[2]\n    content_xpath: ./p\n    first_page_num: 1\n    categories: [it, repos]\n    disabled: true\n    about:\n      website: https://sr.ht\n      wikidata_id: Q78514485\n      official_api_documentation: https://man.sr.ht/\n      use_official_api: false\n      require_api_key: false\n      results: HTML\n\n  - name: goo\n    shortcut: goo\n    engine: xpath\n    paging: true\n    search_url: https://search.goo.ne.jp/web.jsp?MT={query}&FR={pageno}0\n    url_xpath: //div[@class=\"result\"]/p[@class='title fsL1']/a/@href\n    title_xpath: //div[@class=\"result\"]/p[@class='title fsL1']/a\n    content_xpath: //p[contains(@class,'url fsM')]/following-sibling::p\n    first_page_num: 0\n    categories: [general, web]\n    disabled: true\n    timeout: 4.0\n    about:\n      website: https://search.goo.ne.jp\n      wikidata_id: Q249044\n      use_official_api: false\n      require_api_key: false\n      results: HTML\n      language: ja\n\n  - name: bt4g\n    engine: bt4g\n    shortcut: bt4g\n\n  - name: pkg.go.dev\n    engine: pkg_go_dev\n    shortcut: pgo\n    disabled: true\n\n# Doku engine lets you access to any Doku wiki instance:\n# A public one or a privete/corporate one.\n#  - name: ubuntuwiki\n#    engine: doku\n#    shortcut: uw\n#    base_url: 'https://doc.ubuntu-fr.org'\n\n# Be careful when enabling this engine if you are\n# running a public instance. Do not expose any sensitive\n# information. You can restrict access by configuring a list\n# of access tokens under tokens.\n#  - name: git grep\n#    engine: command\n#    command: ['git', 'grep', '{{QUERY}}']\n#    shortcut: gg\n#    tokens: []\n#    disabled: true\n#    delimiter:\n#        chars: ':'\n#        keys: ['filepath', 'code']\n\n# Be careful when enabling this engine if you are\n# running a public instance. Do not expose any sensitive\n# information. You can restrict access by configuring a list\n# of access tokens under tokens.\n#  - name: locate\n#    engine: command\n#    command: ['locate', '{{QUERY}}']\n#    shortcut: loc\n#    tokens: []\n#    disabled: true\n#    delimiter:\n#        chars: ' '\n#        keys: ['line']\n\n# Be careful when enabling this engine if you are\n# running a public instance. Do not expose any sensitive\n# information. You can restrict access by configuring a list\n# of access tokens under tokens.\n#  - name: find\n#    engine: command\n#    command: ['find', '.', '-name', '{{QUERY}}']\n#    query_type: path\n#    shortcut: fnd\n#    tokens: []\n#    disabled: true\n#    delimiter:\n#        chars: ' '\n#        keys: ['line']\n\n# Be careful when enabling this engine if you are\n# running a public instance. Do not expose any sensitive\n# information. You can restrict access by configuring a list\n# of access tokens under tokens.\n#  - name: pattern search in files\n#    engine: command\n#    command: ['fgrep', '{{QUERY}}']\n#    shortcut: fgr\n#    tokens: []\n#    disabled: true\n#    delimiter:\n#        chars: ' '\n#        keys: ['line']\n\n# Be careful when enabling this engine if you are\n# running a public instance. Do not expose any sensitive\n# information. You can restrict access by configuring a list\n# of access tokens under tokens.\n#  - name: regex search in files\n#    engine: command\n#    command: ['grep', '{{QUERY}}']\n#    shortcut: gr\n#    tokens: []\n#    disabled: true\n#    delimiter:\n#        chars: ' '\n#        keys: ['line']\n\ndoi_resolvers:\n  oadoi.org: 'https://oadoi.org/'\n  doi.org: 'https://doi.org/'\n  doai.io: 'https://dissem.in/'\n  sci-hub.se: 'https://sci-hub.se/'\n  sci-hub.st: 'https://sci-hub.st/'\n  sci-hub.ru: 'https://sci-hub.ru/'\n\ndefault_doi_resolver: 'oadoi.org'\n"
  },
  {
    "path": "searxng/uwsgi.ini",
    "content": "[uwsgi]\n# Who will run the code\nuid = searxng\ngid = searxng\n\n# Number of workers (usually CPU count)\n# default value: %k (= number of CPU core, see Dockerfile)\nworkers = %k\n\n# Number of threads per worker\n# default value: 4 (see Dockerfile)\nthreads = 4\n\n# The right granted on the created socket\nchmod-socket = 666\n\n# Plugin to use and interpreter config\nsingle-interpreter = true\nmaster = true\nplugin = python3\nlazy-apps = true\nenable-threads = 4\n\n# Module to import\nmodule = searx.webapp\n\n# Virtualenv and python path\npythonpath = /usr/local/searxng/\nchdir = /usr/local/searxng/searx/\n\n# automatically set processes name to something meaningful\nauto-procname = true\n\n# Disable request logging for privacy\ndisable-logging = true\nlog-5xx = true\n\n# Set the max size of a request (request-body excluded)\nbuffer-size = 8192\n\n# No keep alive\n# See https://github.com/searx/searx-docker/issues/24\nadd-header = Connection: close\n\n# uwsgi serves the static files\nstatic-map = /static=/usr/local/searxng/searx/static\n# expires set to one day\nstatic-expires = /* 86400\nstatic-gzip-all = True\noffload-threads = 4\n"
  },
  {
    "path": "server.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport time\nimport uvicorn\nimport sys\nimport getopt\nimport json\nimport os \nfrom pprint import pprint\nimport requests\nimport trafilatura\nfrom trafilatura import bare_extraction\nfrom concurrent.futures import ThreadPoolExecutor\nimport concurrent\nimport requests\nimport openai\nimport time \nfrom datetime import datetime\nfrom urllib.parse import urlparse\nimport platform\nimport urllib.parse\nimport free_ask_internet\nfrom pydantic import BaseModel, Field\nfrom fastapi import FastAPI, HTTPException\nfrom fastapi.middleware.cors import CORSMiddleware\nfrom contextlib import asynccontextmanager\nfrom typing import Any, Dict, List, Literal, Optional, Union\nfrom sse_starlette.sse import ServerSentEvent, EventSourceResponse\nfrom fastapi.responses import StreamingResponse\n\napp = FastAPI()\n\napp.add_middleware(\n    CORSMiddleware,\n    allow_origins=[\"*\"],\n    allow_credentials=True,\n    allow_methods=[\"*\"],\n    allow_headers=[\"*\"],\n)\n\n\nclass ModelCard(BaseModel):\n    id: str\n    object: str = \"model\"\n    created: int = Field(default_factory=lambda: int(time.time()))\n    owned_by: str = \"owner\"\n    root: Optional[str] = None\n    parent: Optional[str] = None\n    permission: Optional[list] = None\n\n\nclass ModelList(BaseModel):\n    object: str = \"list\"\n    data: List[ModelCard] = []\n\n\nclass ChatMessage(BaseModel):\n    role: Literal[\"user\", \"assistant\", \"system\"]\n    content: str\n\n\nclass DeltaMessage(BaseModel):\n    role: Optional[Literal[\"user\", \"assistant\", \"system\"]] = None\n    content: Optional[str] = None\n\n\nclass QueryRequest(BaseModel):\n    query:str\n    model: str\n    ask_type:  Literal[\"search\", \"llm\"]\n    llm_auth_token: Optional[str] = \"CUSTOM\"\n    llm_base_url: Optional[str] = \"\"\n    using_custom_llm:Optional[bool] = False\n    lang:Optional[str] = \"zh-CN\"\n\nclass ChatCompletionRequest(BaseModel):\n    model: str\n    messages: List[ChatMessage]\n    temperature: Optional[float] = None\n    top_p: Optional[float] = None\n    max_length: Optional[int] = None\n    stream: Optional[bool] = False\n\n\nclass ChatCompletionResponseChoice(BaseModel):\n    index: int\n    message: ChatMessage\n    finish_reason: Literal[\"stop\", \"length\"]\n\n\nclass ChatCompletionResponseStreamChoice(BaseModel):\n    index: int\n    delta: DeltaMessage\n    finish_reason: Optional[Literal[\"stop\", \"length\"]]\n\n\nclass ChatCompletionResponse(BaseModel):\n    model: str\n    object: Literal[\"chat.completion\", \"chat.completion.chunk\"]\n    choices: List[Union[ChatCompletionResponseChoice,\n                        ChatCompletionResponseStreamChoice]]\n    created: Optional[int] = Field(default_factory=lambda: int(time.time()))\n\nclass SearchItem(BaseModel):\n    url: str\n    icon_url: str\n    site_name:str\n    snippet:str\n    title:str \n\nclass SearchItemList(BaseModel):\n    search_items: List[SearchItem] = []\n \nclass SearchResp(BaseModel):\n    code:int\n    msg:str\n    data: List[SearchItem] = []\n \n\n@app.get(\"/v1/models\", response_model=ModelList)\nasync def list_models():\n    global model_args\n    model_card = ModelCard(id=\"gpt-3.5-turbo\")\n    return ModelList(data=[model_card])\n\n\n@app.post(\"/v1/chat/completions\", response_model=ChatCompletionResponse)\nasync def create_chat_completion(request: ChatCompletionRequest):\n    global model, tokenizer\n    print(request)\n    if request.messages[-1].role != \"user\":\n        raise HTTPException(status_code=400, detail=\"Invalid request\")\n    query = request.messages[-1].content\n\n\n    generate = predict(query, \"\", request.model)\n    return EventSourceResponse(generate, media_type=\"text/event-stream\")\n\n \n\ndef predict(query: str, history: None, model_id: str):\n    choice_data = ChatCompletionResponseStreamChoice(\n        index=0,\n        delta=DeltaMessage(role=\"assistant\"),\n        finish_reason=None\n    )\n    chunk = ChatCompletionResponse(model=model_id, choices=[\n                                choice_data], object=\"chat.completion.chunk\")\n    yield \"{}\".format(chunk.json(exclude_unset=True))\n    new_response = \"\"\n    current_length = 0\n    for token in free_ask_internet.ask_internet(query=query):\n    \n        new_response += token\n        if len(new_response) == current_length:\n            continue\n\n        new_text = new_response[current_length:]\n        current_length = len(new_response)\n\n        choice_data = ChatCompletionResponseStreamChoice(\n            index=0,\n            delta=DeltaMessage(content=new_text,role=\"assistant\"),\n            finish_reason=None\n        )\n        chunk = ChatCompletionResponse(model=model_id, choices=[\n                                       choice_data], object=\"chat.completion.chunk\")\n        yield \"{}\".format(chunk.json(exclude_unset=True))\n\n    choice_data = ChatCompletionResponseStreamChoice(\n        index=0,\n        delta=DeltaMessage(),\n        finish_reason=\"stop\"\n    )\n    chunk = ChatCompletionResponse(model=model_id, choices=[\n                                   choice_data], object=\"chat.completion.chunk\")\n    yield \"{}\".format(chunk.json(exclude_unset=True))\n    yield '[DONE]'\n \n\n\n@app.post(\"/api/search/get_search_refs\", response_model=SearchResp)\nasync def get_search_refs(request: QueryRequest):\n\n    global search_results\n    search_results = []\n    search_item_list = []\n    if request.ask_type == \"search\":\n        search_links,search_results = free_ask_internet.search_web_ref(request.query)\n        for search_item in search_links:\n            snippet = search_item.get(\"snippet\")\n            url = search_item.get(\"url\")\n            icon_url = search_item.get(\"icon_url\")\n            site_name = search_item.get(\"site_name\")\n            title = search_item.get(\"title\")\n    \n\n            si = SearchItem(snippet=snippet,url=url,icon_url=icon_url,site_name=site_name,title=title)\n\n            search_item_list.append(si)\n\n    resp = SearchResp(code=0,msg=\"success\",data=search_item_list)\n   \n    return  resp\n\ndef generator(prompt:str, model:str, llm_auth_token:str,llm_base_url:str, using_custom_llm=False,is_failed=False):\n    if is_failed:\n        yield \"搜索失败，没有返回结果\"\n    else:\n        total_token = \"\"\n        for token in  free_ask_internet.chat(prompt=prompt,model=model,llm_auth_token=llm_auth_token,llm_base_url=llm_base_url,using_custom_llm=using_custom_llm,stream=True):\n            total_token += token\n            yield token\n \n@app.post(\"/api/search/stream/{search_uuid}\")\nasync def stream(search_uuid:str,request: QueryRequest):\n    global search_results\n\n    if request.ask_type == \"llm\":\n            \n        answer_language = ' Simplified Chinese '\n        if request.lang == \"zh-CN\":\n            answer_language = ' Simplified Chinese '\n        if request.lang == \"zh-TW\":\n            answer_language = ' Traditional Chinese '\n        if request.lang == \"en-US\":\n            answer_language = ' English '\n        prompt = ' You are a large language AI assistant develop by nash_su. Answer user question in ' + answer_language + '. And here is the user question: ' + request.query\n        generate = generator(prompt,model=request.model,llm_auth_token=request.llm_auth_token, llm_base_url=request.llm_base_url, using_custom_llm=request.using_custom_llm)\n    else:\n        prompt = None\n        limit_count = 10\n\n        while limit_count > 0:\n            try:\n                if len(search_results) > 0:\n                    prompt = free_ask_internet.gen_prompt(request.query,search_results,lang=request.lang,context_length_limit=8000)\n                    break\n                else:\n                    limit_count -= 1\n                    time.sleep(1)\n            except Exception as err:\n                limit_count -= 1\n                time.sleep(1)\n        total_token =  \"\"\n        if prompt:   \n            generate = generator(prompt,model=request.model,llm_auth_token=request.llm_auth_token, llm_base_url=request.llm_base_url, using_custom_llm=request.using_custom_llm)\n        else:\n            generate = generator(prompt,model=request.model,llm_auth_token=request.llm_auth_token,llm_base_url=request.llm_base_url, using_custom_llm=request.using_custom_llm,is_failed=True)\n\n    # return EventSourceResponse(generate, media_type=\"text/event-stream\")\n    return StreamingResponse(generate, media_type=\"text/event-stream\")\n\ndef main():\n\n    port = 8000\n\n    search_results = []\n \n   \n    uvicorn.run(app, host='0.0.0.0', port=port, workers=1)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  }
]