赞
踩
这篇文章起源于小毕超的《无需 GPU 服务器,借助 OpenRouter 零成本搭建自己的大模型助手-CSDN博客》。关于OpenRouter的介绍,此文章已经比较全面。该文章中使用 Python + tornado
实现 Web
服务,前端使用基础的 Html + Jquery编写的
server.py、app.py和html文件为我提供了重要帮助。之所以写这篇文章,是因为在使用server.py中出现了“405 服务接口调用异常”。在利用chrome devtool跟踪网络请求后,发现tornado.web.Application的“/assistant”路由,在向app.py发起post请求之后,还将发起第二次options请求,而文章中的Assistant并没有加入响应options请求的处理代码。
经询问kimi AIKimi.ai - 帮你看更大的世界,了解问题出在“在使用Tornado搭建的服务端时,如果遇到发起OPTIONS请求出错的问题,这通常是由于预请求(pre-flight request)导致的。在HTTP协议中,OPTIONS请求是一种预请求,用于客户端在发送实际请求之前,询问服务器是否允许某个HTTP方法(如POST、GET、PUT、DELETE等)”。解决方案为:
配置CORS:可以使用Tornado的tornado.web.CorsFilter
来设置CORS策略,或者手动设置响应头Access-Control-Allow-Methods
。
添加OPTIONS请求处理器:在Tornado中,可以为路由添加一个OPTIONS方法的处理器,返回允许的HTTP方法。
server.py中对cors的设置已经由set_default_headers正确设置,但Assistant类中缺少options处理方法。修改后的server.py代码如下:
- from tornado.concurrent import run_on_executor
- from tornado.web import RequestHandler
- import tornado.gen
- from openai import OpenAI
- import json
-
- class Assistant(RequestHandler):
- model = "google/gemma-7b-it:free"
- client = OpenAI(
- base_url="https://openrouter.ai/api/v1",
- api_key="sk-##########################",
- )
- default_prompt = "You are an AI assistant that helps people find information."
-
- def prepare(self):
- self.executor = self.application.pool
-
- def set_default_headers(self):
- self.set_header('Access-Control-Allow-Origin', "*")
- self.set_header('Access-Control-Allow-Headers', "Origin, X-Requested-With, Content-Type, Accept")
- self.set_header('Access-Control-Allow-Methods', "GET, POST, PUT, DELETE, OPTIONS")
-
- def options(self):
- # 直接调用set_default_headers来设置CORS头部
- self.set_default_headers()
- # OPTIONS请求不需要响应体,因此直接finish
- self.finish()
-
- @tornado.gen.coroutine
- def post(self):
- json_data = json.loads(self.request.body)
- if 'questions' not in json_data or 'history' not in json_data:
- self.write({
- "code": 400,
- "message": "缺少必填参数"
- })
- return
- questions = json_data['questions']
- history = json_data['history']
- result = yield self.do_handler(questions, history)
- self.write(result)
-
- @run_on_executor
- def do_handler(self, questions, history):
- try:
- answer, history = self.llm(questions, history)
- return {
- "code": 200,
- "message": "success",
- "answer": answer,
- "history": history
- }
- except Exception as e:
- return {
- "code": 400,
- "message": str(e)
- }
-
- def llm(self, user_prompt, messages, system_prompt=default_prompt):
- if not messages:
- messages = []
- messages.append({"role": "user", "content": user_prompt})
- completion = self.client.chat.completions.create(
- extra_headers={
- "HTTP-Referer": "http://localhost:8088",
- "X-Title": "test",
- },
- model=self.model,
- messages=messages,
- max_tokens=2048
- )
- answer = completion.choices[0].message.content
- messages.append({"role": "assistant", "content": answer})
- return answer, messages
-
app.py和本地html文件无需更改。
最后,openrouter中还有其他免费的LLM,但是免费的LLM都有一定的限制,google/gemma-7b-it:free在处理中文方面还存在不足,会出现中英文混排输出和乱码的问题。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。