init repo & basic func

This commit is contained in:
nash_su
2024-04-05 15:15:41 +08:00
parent 17ef565fe9
commit 68d5c3d7b8
8 changed files with 2859 additions and 0 deletions
+9
View File
@@ -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.
+41
View File
@@ -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'
+190
View File
@@ -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
+39
View File
@@ -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
+2361
View File
File diff suppressed because it is too large Load Diff
+50
View File
@@ -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
+169
View File
@@ -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()