han145 commited on
Commit
6887add
·
verified ·
1 Parent(s): cd60a14

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +89 -64
app.py CHANGED
@@ -1,74 +1,99 @@
1
- import gradio as gr
2
- from transformers import AutoTokenizer, AutoModelForCausalLM
3
- import torch
4
- import json
 
 
 
 
5
 
6
- # 全局变量,避免重复加载
7
- model = None
8
- tokenizer = None
9
 
10
- def load_model():
11
- """加载模型和分词器"""
12
- global model, tokenizer
13
- model_name = "deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B"
14
- try:
15
- tokenizer = AutoTokenizer.from_pretrained(model_name)
16
- model = AutoModelForCausalLM.from_pretrained(
17
- model_name,
18
- torch_dtype=torch.float16, # 使用半精度减少内存占用
19
- device_map="auto", # 自动分配至CPU
20
- low_cpu_mem_usage=True # 优化CPU内存使用
21
- )
22
- print("模型加载成功!")
23
- except Exception as e:
24
- print(f"模型加载失败: {e}")
25
 
26
- def openai_compatible_api(message, history):
27
- """处理OpenAI格式的请求"""
28
- if model is None:
29
- load_model()
30
 
31
- # 将历史记录和当前消息组合成对话格式
32
- # 这里需要根据DeepSeek模型要求的模板格式化工序
33
- # 以下是一个简化示例,您需要根据模型的具体要求调整
34
- prompt = f"\n\nHuman: {message}\n\nAssistant:"
 
 
 
 
35
 
36
- inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=1024)
 
 
 
37
 
38
- with torch.no_grad():
39
- outputs = model.generate(
40
- **inputs,
41
- max_new_tokens=512,
42
- temperature=0.7,
43
- top_p=0.95,
44
- do_sample=True,
45
- pad_token_id=tokenizer.eos_token_id
46
- )
47
 
48
- response = tokenizer.decode(outputs[0], skip_special_tokens=True)
49
- # 提取模型生成的部分
50
- generated_text = response.split("Assistant:")[-1].strip()
51
 
52
- # 返回OpenAI兼容格式
53
- return {
54
- "choices": [{
55
- "message": {
56
- "role": "assistant",
57
- "content": generated_text
 
 
 
 
 
58
  }
59
- }]
60
- }
61
-
62
- # 在Gradio界面启动前加载模型(可选,或等待第一个请求时加载)
63
- load_model()
64
 
65
- # 创建Gradio界面,但隐藏默认的Web UI,专注于API
66
- demo = gr.ChatInterface(
67
- fn=openai_compatible_api,
68
- title="DeepSeek API Service",
69
- description="OpenAI-compatible API for DeepSeek-R1"
70
- )
71
-
72
- # 禁用自动创建公共链接,仅以API模式运行
73
- if __name__ == "__main__":
74
- demo.launch(show_api=True, server_name="0.0.0.0", server_port=7860)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from fastapi import FastAPI, HTTPException, Request
3
+ from fastapi.responses import StreamingResponse, JSONResponse
4
+ from pydantic import BaseModel, Field
5
+ from typing import List, Optional, Union, Dict, Any
6
+ from huggingface_hub import hf_hub_download
7
+ from llama_cpp import Llama
8
+ import time
9
 
10
+ app = FastAPI(title="OpenAI Compatible API for OpenClaw")
 
 
11
 
12
+ # 配置模型:使用 Qwen2.5 1.5B 4位量化版,CPU 运行极快且仅占约 1.5G 内存
13
+ REPO_ID = "Qwen/Qwen2.5-1.5B-Instruct-GGUF"
14
+ FILENAME = "qwen2.5-1.5b-instruct-q4_k_m.gguf"
15
+ MODEL_NAME = "qwen2.5-1.5b"
 
 
 
 
 
 
 
 
 
 
 
16
 
17
+ print("正在从 Hugging Face 下载 GGUF 模型文件...")
18
+ model_path = hf_hub_download(repo_id=REPO_ID, filename=FILENAME)
19
+ print(os.path.abspath(model_path))
20
+ print("模型下载完成,正在加载至内存...")
21
 
22
+ # 初始化 Llama-cpp (配置上下文长度为 2048 以节省内存)
23
+ llm = Llama(
24
+ model_path=model_path,
25
+ n_ctx=2048,
26
+ n_threads=2, # 免费容器为 2核 CPU
27
+ chat_format="chatml"
28
+ )
29
+ print("模型加载成功!服务已就绪。")
30
 
31
+ # --- OpenAI 协议数据结构定义 ---
32
+ class ChatMessage(BaseModel):
33
+ role: str
34
+ content: str
35
 
36
+ class ChatCompletionRequest(BaseModel):
37
+ model: str
38
+ messages: List[ChatMessage]
39
+ temperature: Optional[float] = 0.7
40
+ top_p: Optional[float] = 0.9
41
+ max_tokens: Optional[int] = 512
42
+ stream: Optional[bool] = False
 
 
43
 
44
+ @app.get("/")
45
+ def index():
46
+ return {"status": "running", "compatible_with": "OpenClaw / OpenAI API"}
47
 
48
+ # 1. 适配 OpenClaw 获取模型列表的接口
49
+ @app.get("/v1/models")
50
+ async def list_models():
51
+ return JSONResponse({
52
+ "object": "list",
53
+ "data": [
54
+ {
55
+ "id": MODEL_NAME,
56
+ "object": "model",
57
+ "created": int(time.time()),
58
+ "owned_by": "huggingface"
59
  }
60
+ ]
61
+ })
 
 
 
62
 
63
+ # 2. 适配 OpenClaw 聊天对话接口 (支持流式和非流式)
64
+ @app.post("/v1/chat/completions")
65
+ async def chat_completions(request: ChatCompletionRequest):
66
+ # 将标准 OpenAI 消息格式转换为 llama-cpp 接收的格式
67
+ llama_messages = [{"role": msg.role, "content": msg.content} for msg in request.messages]
68
+
69
+ try:
70
+ if request.stream:
71
+ # 流式传输实现
72
+ chunks = llm.create_chat_completion(
73
+ messages=llama_messages,
74
+ temperature=request.temperature,
75
+ top_p=request.top_p,
76
+ max_tokens=request.max_tokens,
77
+ stream=True
78
+ )
79
+ def stream_generator():
80
+ for chunk in chunks:
81
+ import json
82
+ yield f"data: {json.dumps(chunk)}\n\n"
83
+ yield "data: [DONE]\n\n"
84
+ return StreamingResponse(stream_generator(), media_type="text/event-stream")
85
+ else:
86
+ # 非流式传输实现
87
+ response = llm.create_chat_completion(
88
+ messages=llama_messages,
89
+ temperature=request.temperature,
90
+ top_p=request.top_p,
91
+ max_tokens=request.max_tokens,
92
+ stream=False
93
+ )
94
+ # 强制覆盖返回的模型名称,确保与 OpenClaw 请求的一致
95
+ response["model"] = MODEL_NAME
96
+ return JSONResponse(response)
97
+
98
+ except Exception as e:
99
+ raise HTTPException(status_code=500, detail=str(e))