mirror of
https://github.com/wahyd4/FreeAskInternet.git
synced 2026-08-08 21:00:14 +10:00
init repo & basic func
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
FROM python:3.9.15
|
||||
|
||||
WORKDIR /app
|
||||
COPY requirements.txt /app
|
||||
RUN pip3 install -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com -r requirements.txt --no-cache-dir
|
||||
COPY . /app
|
||||
EXPOSE 8000
|
||||
ENTRYPOINT ["python3"]
|
||||
CMD ["server.py"]
|
||||
Binary file not shown.
@@ -0,0 +1,41 @@
|
||||
services:
|
||||
backend:
|
||||
image: docker.io/nashsu/free_ask_internet:latest
|
||||
depends_on:
|
||||
- freegpt35
|
||||
restart: on-failure
|
||||
|
||||
|
||||
chatgpt-next-web:
|
||||
image: yidadaa/chatgpt-next-web
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
OPENAI_API_KEY: "FreeAskInternet"
|
||||
# CODE: "FreeAskInternet" # 如果你想要设置页面的访问密码,请修改这里
|
||||
BASE_URL: "http://backend:8000"
|
||||
CUSTOM_MODELS: "-all,+gpt-3.5-turbo"
|
||||
depends_on:
|
||||
- freegpt35
|
||||
|
||||
freegpt35:
|
||||
image: missuo/freegpt35:latest
|
||||
restart: always
|
||||
|
||||
searxng:
|
||||
image: docker.io/searxng/searxng:latest
|
||||
volumes:
|
||||
- ./searxng:/etc/searxng:rw
|
||||
environment:
|
||||
- SEARXNG_BASE_URL=https://${SEARXNG_HOSTNAME:-localhost}/
|
||||
cap_drop:
|
||||
- ALL
|
||||
cap_add:
|
||||
- CHOWN
|
||||
- SETGID
|
||||
- SETUID
|
||||
logging:
|
||||
driver: 'json-file'
|
||||
options:
|
||||
max-size: '1m'
|
||||
max-file: '1'
|
||||
@@ -0,0 +1,190 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import json
|
||||
import os
|
||||
from pprint import pprint
|
||||
import requests
|
||||
import trafilatura
|
||||
from trafilatura import bare_extraction
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
import concurrent
|
||||
import requests
|
||||
import openai
|
||||
import time
|
||||
from datetime import datetime
|
||||
from urllib.parse import urlparse
|
||||
import tldextract
|
||||
import platform
|
||||
import urllib.parse
|
||||
|
||||
|
||||
def extract_url_content(url):
|
||||
downloaded = trafilatura.fetch_url(url)
|
||||
content = trafilatura.extract(downloaded)
|
||||
|
||||
return {"url":url, "content":content}
|
||||
|
||||
|
||||
|
||||
|
||||
def search_web_ref(query:str, debug=False):
|
||||
|
||||
content_list = []
|
||||
|
||||
try:
|
||||
|
||||
safe_string = urllib.parse.quote_plus(":all !general " + query)
|
||||
|
||||
response = requests.get('http://searxng:8080?q=' + safe_string + '&format=json')
|
||||
response.raise_for_status()
|
||||
search_results = response.json()
|
||||
|
||||
if debug:
|
||||
print("JSON Response:")
|
||||
pprint(search_results)
|
||||
pedding_urls = []
|
||||
|
||||
conv_links = []
|
||||
|
||||
if search_results.get('results'):
|
||||
for item in search_results.get('results')[0:9]:
|
||||
name = item.get('title')
|
||||
snippet = item.get('content')
|
||||
url = item.get('url')
|
||||
pedding_urls.append(url)
|
||||
|
||||
if url:
|
||||
url_parsed = urlparse(url)
|
||||
domain = url_parsed.netloc
|
||||
icon_url = url_parsed.scheme + '://' + url_parsed.netloc + '/favicon.ico'
|
||||
site_name = tldextract.extract(url).domain
|
||||
|
||||
conv_links.append({
|
||||
'site_name':site_name,
|
||||
'icon_url':icon_url,
|
||||
'title':name,
|
||||
'url':url,
|
||||
'snippet':snippet
|
||||
})
|
||||
|
||||
results = []
|
||||
futures = []
|
||||
|
||||
executor = ThreadPoolExecutor(max_workers=10)
|
||||
for url in pedding_urls:
|
||||
futures.append(executor.submit(extract_url_content,url))
|
||||
try:
|
||||
for future in futures:
|
||||
res = future.result(timeout=5)
|
||||
results.append(res)
|
||||
except concurrent.futures.TimeoutError:
|
||||
print("任务执行超时")
|
||||
executor.shutdown(wait=False,cancel_futures=True)
|
||||
|
||||
for content in results:
|
||||
if content and content.get('content'):
|
||||
|
||||
item_dict = {
|
||||
"url":content.get('url'),
|
||||
"content": content.get('content'),
|
||||
"length":len(content.get('content'))
|
||||
}
|
||||
content_list.append(item_dict)
|
||||
if debug:
|
||||
print("URL: {}".format(url))
|
||||
print("=================")
|
||||
|
||||
return content_list
|
||||
except Exception as ex:
|
||||
raise ex
|
||||
|
||||
|
||||
def gen_prompt(question,content_list, context_length_limit=11000,debug=False):
|
||||
|
||||
limit_len = (context_length_limit - 2000)
|
||||
if len(question) > limit_len:
|
||||
question = question[0:limit_len]
|
||||
|
||||
ref_content = [ item.get("content") for item in content_list]
|
||||
|
||||
if len(ref_content) > 0:
|
||||
|
||||
|
||||
prompts = '''
|
||||
您是一位由 nash_su 开发的基于搜索引擎返回内容的AI问答助手。您将被提供一个用户问题,并需要撰写一个清晰、简洁且准确的答案。答案必须正确、精确,并以专家的中立和职业语气撰写。请将答案限制在2000个标记内。不要提供与问题无关的信息,也不要重复。如果给出的上下文信息不足,请在相关主题后写上“信息缺失:”。除非是代码、特定的名称或引用编号,答案的语言应与问题相同。以下是上下文的内容集:
|
||||
''' + "\n\n" + "```"
|
||||
ref_index = 1
|
||||
|
||||
for ref_text in ref_content:
|
||||
|
||||
prompts = prompts + "\n\n" + ref_text
|
||||
ref_index += 1
|
||||
|
||||
if len(prompts) >= limit_len:
|
||||
prompts = prompts[0:limit_len]
|
||||
prompts = prompts + '''
|
||||
```
|
||||
记住,不要一字不差的重复上下文内容. 回答必须使用简体中文,如果回答很长,请尽量结构化、分段落总结。 下面是用户问题:
|
||||
''' + question
|
||||
|
||||
|
||||
else:
|
||||
prompts = question
|
||||
|
||||
if debug:
|
||||
print(prompts)
|
||||
print("总长度:"+ str(len(prompts)))
|
||||
return prompts
|
||||
|
||||
|
||||
|
||||
def chat(prompt, stream=True, debug=False):
|
||||
openai.base_url = "http://freegpt35:3040/v1/"
|
||||
openai.api_key = "EMPTY"
|
||||
total_content = ""
|
||||
for chunk in openai.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
# model='Qwen1.5-1.8B-Chat',
|
||||
messages=[{
|
||||
"role": "user",
|
||||
"content": prompt
|
||||
}],
|
||||
stream=True,
|
||||
max_tokens=1024,temperature=0.2
|
||||
):
|
||||
stream_resp = chunk.dict()
|
||||
token = stream_resp["choices"][0]["delta"].get("content", "")
|
||||
if token:
|
||||
|
||||
total_content += token
|
||||
yield token
|
||||
if debug:
|
||||
print(total_content)
|
||||
|
||||
|
||||
|
||||
|
||||
def ask_internet(query:str, debug=False):
|
||||
|
||||
content_list = search_web_ref(query,debug=debug)
|
||||
prompt = gen_prompt(query,content_list,context_length_limit=8000,debug=debug)
|
||||
total_token = ""
|
||||
|
||||
for token in chat(prompt=prompt):
|
||||
# for token in daxianggpt.chat(prompt=prompt):
|
||||
if token:
|
||||
total_token += token
|
||||
yield token
|
||||
yield "\n\n"
|
||||
# 是否返回参考资料
|
||||
if True:
|
||||
yield "---"
|
||||
yield "\n"
|
||||
yield "参考资料:\n"
|
||||
count = 1
|
||||
for url_content in content_list:
|
||||
url = url_content.get('url')
|
||||
yield "*[{}. {}]({})*".format(str(count),url,url )
|
||||
yield "\n"
|
||||
count += 1
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
annotated-types==0.6.0
|
||||
anyio==4.3.0
|
||||
certifi==2024.2.2
|
||||
charset-normalizer==3.3.2
|
||||
click==8.1.7
|
||||
courlan==1.0.0
|
||||
dateparser==1.2.0
|
||||
distro==1.9.0
|
||||
exceptiongroup==1.2.0
|
||||
fastapi==0.110.1
|
||||
filelock==3.13.3
|
||||
h11==0.14.0
|
||||
htmldate==1.8.0
|
||||
httpcore==1.0.5
|
||||
httpx==0.27.0
|
||||
idna==3.6
|
||||
jusText==3.0.0
|
||||
langcodes==3.3.0
|
||||
lxml==5.1.1
|
||||
openai==1.16.2
|
||||
pydantic==2.6.4
|
||||
pydantic_core==2.16.3
|
||||
python-dateutil==2.9.0.post0
|
||||
pytz==2024.1
|
||||
regex==2023.12.25
|
||||
requests==2.31.0
|
||||
requests-file==2.0.0
|
||||
six==1.16.0
|
||||
sniffio==1.3.1
|
||||
sse-starlette==2.0.0
|
||||
starlette==0.37.2
|
||||
tld==0.13
|
||||
tldextract==5.1.2
|
||||
tqdm==4.66.2
|
||||
trafilatura==1.8.1
|
||||
typing_extensions==4.10.0
|
||||
tzlocal==5.2
|
||||
urllib3==2.2.1
|
||||
uvicorn==0.29.0
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,50 @@
|
||||
[uwsgi]
|
||||
# Who will run the code
|
||||
uid = searxng
|
||||
gid = searxng
|
||||
|
||||
# Number of workers (usually CPU count)
|
||||
# default value: %k (= number of CPU core, see Dockerfile)
|
||||
workers = %k
|
||||
|
||||
# Number of threads per worker
|
||||
# default value: 4 (see Dockerfile)
|
||||
threads = 4
|
||||
|
||||
# The right granted on the created socket
|
||||
chmod-socket = 666
|
||||
|
||||
# Plugin to use and interpreter config
|
||||
single-interpreter = true
|
||||
master = true
|
||||
plugin = python3
|
||||
lazy-apps = true
|
||||
enable-threads = 4
|
||||
|
||||
# Module to import
|
||||
module = searx.webapp
|
||||
|
||||
# Virtualenv and python path
|
||||
pythonpath = /usr/local/searxng/
|
||||
chdir = /usr/local/searxng/searx/
|
||||
|
||||
# automatically set processes name to something meaningful
|
||||
auto-procname = true
|
||||
|
||||
# Disable request logging for privacy
|
||||
disable-logging = true
|
||||
log-5xx = true
|
||||
|
||||
# Set the max size of a request (request-body excluded)
|
||||
buffer-size = 8192
|
||||
|
||||
# No keep alive
|
||||
# See https://github.com/searx/searx-docker/issues/24
|
||||
add-header = Connection: close
|
||||
|
||||
# uwsgi serves the static files
|
||||
static-map = /static=/usr/local/searxng/searx/static
|
||||
# expires set to one day
|
||||
static-expires = /* 86400
|
||||
static-gzip-all = True
|
||||
offload-threads = 4
|
||||
@@ -0,0 +1,169 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import time
|
||||
import uvicorn
|
||||
import sys
|
||||
import getopt
|
||||
import json
|
||||
import os
|
||||
from pprint import pprint
|
||||
import requests
|
||||
import trafilatura
|
||||
from trafilatura import bare_extraction
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
import concurrent
|
||||
import requests
|
||||
import openai
|
||||
import time
|
||||
from datetime import datetime
|
||||
from urllib.parse import urlparse
|
||||
import platform
|
||||
import urllib.parse
|
||||
import free_ask_internet
|
||||
from pydantic import BaseModel, Field
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import Any, Dict, List, Literal, Optional, Union
|
||||
from sse_starlette.sse import ServerSentEvent, EventSourceResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
class ModelCard(BaseModel):
|
||||
id: str
|
||||
object: str = "model"
|
||||
created: int = Field(default_factory=lambda: int(time.time()))
|
||||
owned_by: str = "owner"
|
||||
root: Optional[str] = None
|
||||
parent: Optional[str] = None
|
||||
permission: Optional[list] = None
|
||||
|
||||
|
||||
class ModelList(BaseModel):
|
||||
object: str = "list"
|
||||
data: List[ModelCard] = []
|
||||
|
||||
|
||||
class ChatMessage(BaseModel):
|
||||
role: Literal["user", "assistant", "system"]
|
||||
content: str
|
||||
|
||||
|
||||
class DeltaMessage(BaseModel):
|
||||
role: Optional[Literal["user", "assistant", "system"]] = None
|
||||
content: Optional[str] = None
|
||||
|
||||
|
||||
class ChatCompletionRequest(BaseModel):
|
||||
model: str
|
||||
messages: List[ChatMessage]
|
||||
temperature: Optional[float] = None
|
||||
top_p: Optional[float] = None
|
||||
max_length: Optional[int] = None
|
||||
stream: Optional[bool] = False
|
||||
|
||||
|
||||
class ChatCompletionResponseChoice(BaseModel):
|
||||
index: int
|
||||
message: ChatMessage
|
||||
finish_reason: Literal["stop", "length"]
|
||||
|
||||
|
||||
class ChatCompletionResponseStreamChoice(BaseModel):
|
||||
index: int
|
||||
delta: DeltaMessage
|
||||
finish_reason: Optional[Literal["stop", "length"]]
|
||||
|
||||
|
||||
class ChatCompletionResponse(BaseModel):
|
||||
model: str
|
||||
object: Literal["chat.completion", "chat.completion.chunk"]
|
||||
choices: List[Union[ChatCompletionResponseChoice,
|
||||
ChatCompletionResponseStreamChoice]]
|
||||
created: Optional[int] = Field(default_factory=lambda: int(time.time()))
|
||||
|
||||
|
||||
|
||||
|
||||
@app.get("/v1/models", response_model=ModelList)
|
||||
async def list_models():
|
||||
global model_args
|
||||
model_card = ModelCard(id="gpt-3.5-turbo")
|
||||
return ModelList(data=[model_card])
|
||||
|
||||
|
||||
@app.post("/v1/chat/completions", response_model=ChatCompletionResponse)
|
||||
async def create_chat_completion(request: ChatCompletionRequest):
|
||||
global model, tokenizer
|
||||
print(request)
|
||||
if request.messages[-1].role != "user":
|
||||
raise HTTPException(status_code=400, detail="Invalid request")
|
||||
query = request.messages[-1].content
|
||||
|
||||
|
||||
generate = predict(query, "", request.model)
|
||||
return EventSourceResponse(generate, media_type="text/event-stream")
|
||||
|
||||
|
||||
|
||||
def predict(query: str, history: None, model_id: str):
|
||||
choice_data = ChatCompletionResponseStreamChoice(
|
||||
index=0,
|
||||
delta=DeltaMessage(role="assistant"),
|
||||
finish_reason=None
|
||||
)
|
||||
chunk = ChatCompletionResponse(model=model_id, choices=[
|
||||
choice_data], object="chat.completion.chunk")
|
||||
yield "{}".format(chunk.json(exclude_unset=True))
|
||||
new_response = ""
|
||||
current_length = 0
|
||||
for token in free_ask_internet.ask_internet(query=query):
|
||||
|
||||
new_response += token
|
||||
if len(new_response) == current_length:
|
||||
continue
|
||||
|
||||
new_text = new_response[current_length:]
|
||||
current_length = len(new_response)
|
||||
|
||||
choice_data = ChatCompletionResponseStreamChoice(
|
||||
index=0,
|
||||
delta=DeltaMessage(content=new_text,role="assistant"),
|
||||
finish_reason=None
|
||||
)
|
||||
chunk = ChatCompletionResponse(model=model_id, choices=[
|
||||
choice_data], object="chat.completion.chunk")
|
||||
yield "{}".format(chunk.json(exclude_unset=True))
|
||||
|
||||
choice_data = ChatCompletionResponseStreamChoice(
|
||||
index=0,
|
||||
delta=DeltaMessage(),
|
||||
finish_reason="stop"
|
||||
)
|
||||
chunk = ChatCompletionResponse(model=model_id, choices=[
|
||||
choice_data], object="chat.completion.chunk")
|
||||
yield "{}".format(chunk.json(exclude_unset=True))
|
||||
yield '[DONE]'
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
port = 8000
|
||||
|
||||
|
||||
|
||||
uvicorn.run(app, host='0.0.0.0', port=port, workers=1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user