From 5bcfbdb88d10403b2457eccfaa620610af91e913 Mon Sep 17 00:00:00 2001 From: freedakgmail Date: Mon, 3 Aug 2026 07:57:31 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E7=A4=BA=E8=8C=83=E5=92=8C=E5=90=AF=E5=8A=A8=E6=8C=87=E5=8D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - examples/demo_usage.py: Python 使用示范(openai SDK + requests) - examples/demo_usage.go: Go 使用示范(net/http) - run.md: 完整启动指南(前置条件、3种启动方式、API Key 配置、验证命令) --- examples/demo_usage.go | 141 +++++++++++++++++++++++++++++++ examples/demo_usage.py | 184 +++++++++++++++++++++++++++++++++++++++++ run.md | 154 ++++++++++++++++++++++++++++++++++ 3 files changed, 479 insertions(+) create mode 100644 examples/demo_usage.go create mode 100644 examples/demo_usage.py create mode 100644 run.md diff --git a/examples/demo_usage.go b/examples/demo_usage.go new file mode 100644 index 0000000..3e8b7e4 --- /dev/null +++ b/examples/demo_usage.go @@ -0,0 +1,141 @@ +// Edge AI Gateway 使用示范 (Go 版本) +// +// 演示 Go 应用如何调用 Edge AI Gateway 的 OpenAI 兼容 API。 +// +// 运行:go run examples/demo_usage.go +package main + +import ( + "bufio" + "encoding/json" + "fmt" + "io" + "net/http" + "strings" +) + +const ( + BaseURL = "http://localhost:8080" + APIKey = "test-key" +) + +func main() { + // === 1. 非流式对话 === + fmt.Println("=== 非流式对话 ===") + resp := doPost(BaseURL+"/v1/chat/completions", map[string]any{ + "model": "general-chat", + "messages": []map[string]string{{"role": "user", "content": "你好,介绍一下你自己"}}, + "stream": false, + }) + fmt.Printf("回复: %s\n", resp["choices"].([]any)[0].(map[string]any)["message"].(map[string]any)["content"]) + fmt.Printf("模型: %s\n", resp["actual_model"]) + fmt.Println() + + // === 2. 流式对话 (SSE) === + fmt.Println("=== 流式对话 ===") + doStream(BaseURL+"/v1/chat/completions", map[string]any{ + "model": "fast-chat", + "messages": []map[string]string{{"role": "user", "content": "写一个Go hello world"}}, + "stream": true, + }) + fmt.Println() + + // === 3. 查看模型列表 === + fmt.Println("=== 可用模型 ===") + resp2 := doGet(BaseURL + "/v1/models") + for _, m := range resp2["data"].([]any) { + fmt.Printf(" - %s\n", m.(map[string]any)["id"]) + } + fmt.Println() + + // === 4. 会话管理 === + fmt.Println("=== 会话管理 ===") + sess := doPost(BaseURL+"/v1/sessions", map[string]any{ + "application_id": "demo-app", + "user_id": "user1", + }) + sessionID := sess["session_id"].(string) + fmt.Printf("创建会话: %s\n\n", sessionID) + + // === 5. 错误处理 === + fmt.Println("=== 错误处理 ===") + // 无认证 + resp3, _ := http.Get(BaseURL + "/v1/models") + fmt.Printf("无认证: HTTP %d\n", resp3.StatusCode) + resp3.Body.Close() + // 缺少 model + resp4 := doPost(BaseURL+"/v1/chat/completions", map[string]any{ + "messages": []map[string]string{{"role": "user", "content": "hi"}}, + }) + if err, ok := resp4["error"]; ok { + fmt.Printf("缺少model: %s\n", err.(map[string]any)["code"]) + } + + fmt.Println("\n✅ 示范完成!") +} + +func doPost(url string, body map[string]any) map[string]any { + b, _ := json.Marshal(body) + req, _ := http.NewRequest("POST", url, strings.NewReader(string(b))) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+APIKey) + resp, err := http.DefaultClient.Do(req) + if err != nil { + fmt.Printf("请求失败: %v\n", err) + return nil + } + defer resp.Body.Close() + data, _ := io.ReadAll(resp.Body) + var result map[string]any + json.Unmarshal(data, &result) + return result +} + +func doGet(url string) map[string]any { + req, _ := http.NewRequest("GET", url, nil) + req.Header.Set("Authorization", "Bearer "+APIKey) + resp, err := http.DefaultClient.Do(req) + if err != nil { + fmt.Printf("请求失败: %v\n", err) + return nil + } + defer resp.Body.Close() + data, _ := io.ReadAll(resp.Body) + var result map[string]any + json.Unmarshal(data, &result) + return result +} + +func doStream(url string, body map[string]any) { + b, _ := json.Marshal(body) + req, _ := http.NewRequest("POST", url, strings.NewReader(string(b))) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+APIKey) + resp, err := http.DefaultClient.Do(req) + if err != nil { + fmt.Printf("请求失败: %v\n", err) + return + } + defer resp.Body.Close() + scanner := bufio.NewScanner(resp.Body) + for scanner.Scan() { + line := scanner.Text() + if !strings.HasPrefix(line, "data: ") { + continue + } + data := strings.TrimPrefix(line, "data: ") + if data == "[DONE]" { + return + } + var chunk map[string]any + json.Unmarshal([]byte(data), &chunk) + choices := chunk["choices"].([]any) + delta := choices[0].(map[string]any)["delta"].(map[string]any) + if content, ok := delta["content"]; ok { + fmt.Print(content.(string)) + } + } + if err := scanner.Err(); err != nil { + fmt.Printf("\nstream error: %v\n", err) + } +} diff --git a/examples/demo_usage.py b/examples/demo_usage.py new file mode 100644 index 0000000..19d1329 --- /dev/null +++ b/examples/demo_usage.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python3 +""" +Edge AI Gateway 使用示范 + +本文件演示其他应用如何通过 OpenAI 兼容 API 调用 Edge AI Gateway。 +支持两种方式: +1. 使用 openai SDK(推荐) +2. 使用 requests 库直接调用 + +运行前请确保: +- Edge AI Gateway 已启动 (默认 http://localhost:8080) +- 已配置有效的 API Key +""" + +# ============================================================ +# 方式一:使用 openai SDK(推荐) +# ============================================================ +# pip install openai + +from openai import OpenAI + +client = OpenAI( + base_url="http://localhost:8080/v1", + api_key="test-key", # 替换为你的 API Key +) + +# --- 非流式对话 --- +print("=== 非流式对话 ===") +response = client.chat.completions.create( + model="general-chat", # 逻辑模型名,网关自动路由到实际模型 + messages=[ + {"role": "system", "content": "你是一个简洁的助手"}, + {"role": "user", "content": "你好,介绍一下你自己"}, + ], + stream=False, +) +print(f"回复: {response.choices[0].message.content}") +print(f"Token: 输入={response.usage.prompt_tokens}, 输出={response.usage.completion_tokens}") +print() + +# --- 流式对话 (SSE) --- +print("=== 流式对话 ===") +stream = client.chat.completions.create( + model="fast-chat", + messages=[ + {"role": "user", "content": "写一个 Python 的 hello world"}, + ], + stream=True, +) +for chunk in stream: + delta = chunk.choices[0].delta.content + if delta: + print(delta, end="", flush=True) +print("\n") + +# --- 带优先级的请求 (P0 最高) --- +print("=== 高优先级请求 ===") +response = client.chat.completions.create( + model="general-chat", + messages=[{"role": "user", "content": "1+1=?"}], + extra_headers={"X-Priority": "P0"}, # 通过 header 传递优先级 +) +print(f"回复: {response.choices[0].message.content}") +print() + +# --- 查看可用模型 --- +print("=== 可用模型 ===") +models = client.models.list() +for m in models.data: + print(f" - {m.id}") +print() + + +# ============================================================ +# 方式二:使用 requests 库直接调用 +# ============================================================ +# pip install requests + +import requests + +BASE_URL = "http://localhost:8080" +API_KEY = "test-key" +HEADERS = { + "Authorization": f"Bearer {API_KEY}", + "Content-Type": "application/json", +} + +# --- 非流式 --- +print("=== requests: 非流式 ===") +resp = requests.post( + f"{BASE_URL}/v1/chat/completions", + headers=HEADERS, + json={ + "model": "general-chat", + "messages": [{"role": "user", "content": "你好"}], + "stream": False, + }, +) +data = resp.json() +print(f"状态: {data.get('status')}") +print(f"回复: {data['choices'][0]['message']['content']}") +print(f"模型: {data.get('actual_model')}") +print() + +# --- 流式 (SSE) --- +print("=== requests: 流式 ===") +resp = requests.post( + f"{BASE_URL}/v1/chat/completions", + headers=HEADERS, + json={ + "model": "fast-chat", + "messages": [{"role": "user", "content": "讲个笑话"}], + "stream": True, + }, + stream=True, +) +for line in resp.iter_lines(): + if line: + line = line.decode("utf-8") + if line.startswith("data: ") and line != "data: [DONE]": + import json + chunk = json.loads(line[6:]) + delta = chunk["choices"][0]["delta"].get("content", "") + if delta: + print(delta, end="", flush=True) +print("\n") + +# --- 会话管理 --- +print("=== 会话管理 ===") +# 创建会话 +resp = requests.post( + f"{BASE_URL}/v1/sessions", + headers=HEADERS, + json={"application_id": "my-app", "user_id": "user123"}, +) +session = resp.json() +session_id = session["session_id"] +print(f"创建会话: {session_id}") + +# 查询会话 +resp = requests.get( + f"{BASE_URL}/v1/sessions/{session_id}", + headers=HEADERS, +) +print(f"会话信息: {resp.json()}") + +# 删除会话 +resp = requests.delete( + f"{BASE_URL}/v1/sessions/{session_id}", + headers=HEADERS, +) +print(f"删除会话: HTTP {resp.status_code}") +print() + +# --- 错误处理 --- +print("=== 错误处理 ===") +# 无认证 +resp = requests.get(f"{BASE_URL}/v1/models") +print(f"无认证: {resp.status_code} - {resp.json()['error']['code']}") + +# 无效 key +resp = requests.get( + f"{BASE_URL}/v1/models", + headers={"Authorization": "Bearer invalid-key"}, +) +print(f"无效key: {resp.status_code} - {resp.json()['error']['code']}") + +# 缺少 model +resp = requests.post( + f"{BASE_URL}/v1/chat/completions", + headers=HEADERS, + json={"messages": [{"role": "user", "content": "hi"}]}, +) +print(f"缺少model: {resp.status_code} - {resp.json()['error']['code']}") + +# 未知 model +resp = requests.post( + f"{BASE_URL}/v1/chat/completions", + headers=HEADERS, + json={"model": "nonexistent", "messages": [{"role": "user", "content": "hi"}]}, +) +print(f"未知model: {resp.status_code} - {resp.json()['error']['code']}") + +print("\n✅ 示范完成!") diff --git a/run.md b/run.md new file mode 100644 index 0000000..ff0a1eb --- /dev/null +++ b/run.md @@ -0,0 +1,154 @@ +# Edge AI Gateway 启动指南 + +## 前置条件 + +### 1. 安装 Go + +```bash +# macOS +brew install go + +# Linux +wget https://go.dev/dl/go1.23.0.linux-amd64.tar.gz +sudo tar -C /usr/local -xzf go1.23.0.linux-amd64.tar.gz +export PATH=$PATH:/usr/local/go/bin +``` + +### 2. 安装 Ollama(本地推理引擎) + +```bash +# macOS +brew install ollama + +# Linux +curl -fsSL https://ollama.com/install.sh | sh + +# 拉取模型 +ollama pull deepseek-r1:1.5b +``` + +### 3. 安装 SQLite + +```bash +# macOS(自带) +# Linux +sudo apt install sqlite3 +``` + +## 启动方式 + +### 方式一:直接运行 + +```bash +# 1. 创建数据目录 +mkdir -p /tmp/edgeai-data + +# 2. 启动 Ollama(另一个终端) +ollama serve + +# 3. 启动 Edge AI Gateway +EDGEAI_DB_PATH=/tmp/edgeai-data go run ./cmd/gateway/ --config configs/config.yaml +``` + +服务器将在 `http://0.0.0.0:8080` 启动。 + +### 方式二:编译后运行 + +```bash +# 1. 编译 +go build -o bin/gateway ./cmd/gateway/ + +# 2. 启动 +EDGEAI_DB_PATH=/tmp/edgeai-data ./bin/gateway --config configs/config.yaml +``` + +### 方式三:Docker Compose + +```bash +cd deploy +docker-compose up -d +``` + +服务端口: +- Gateway: http://localhost:8080 +- Ollama: http://localhost:11434 +- Prometheus: http://localhost:9090 +- Grafana: http://localhost:3000 + +## 配置 API Key + +首次启动后需要创建 API Key: + +```bash +# 1. 计算 key 的 SHA-256 哈希 +HASH=$(echo -n "your-api-key" | shasum -a 256 | awk '{print $1}') + +# 2. 插入数据库 +sqlite3 /tmp/edgeai-data/auth.db "INSERT INTO api_keys (app_id, tenant_id, name, key_hash, allowed_models, allowed_priorities, is_admin, enabled) VALUES ('my-app', 'my-tenant', 'my-key', '$HASH', '[]', '[]', 1, 1);" + +# 3. 重启服务器使 key 生效 +``` + +## 环境变量 + +| 变量 | 默认值 | 说明 | +|------|--------|------| +| `EDGEAI_DB_PATH` | `/var/lib/edgeai` | 数据库目录 | +| `EDGEAI_SERVER_PORT` | `8080` | 服务端口 | +| `EDGEAI_LOG_LEVEL` | `info` | 日志级别 | +| `EDGEAI_CONFIG_PATH` | `configs/config.yaml` | 配置文件路径 | + +## 验证 + +```bash +# 健康检查 +curl http://localhost:8080/health + +# 查看模型(需 API Key) +curl -H "Authorization: Bearer your-api-key" http://localhost:8080/v1/models + +# 非流式对话 +curl -X POST http://localhost:8080/v1/chat/completions \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer your-api-key" \ + -d '{"model":"general-chat","messages":[{"role":"user","content":"你好"}]}' + +# 流式对话 +curl -N -X POST http://localhost:8080/v1/chat/completions \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer your-api-key" \ + -d '{"model":"fast-chat","messages":[{"role":"user","content":"你好"}],"stream":true}' + +# Prometheus 指标 +curl http://localhost:8080/metrics +``` + +## 可选:启动 vLLM + +```bash +# 安装 vLLM +pip install vllm + +# 启动 vLLM 服务(端口 8000) +vllm serve --model deepseek-ai/deepseek-r1-1.5b --port 8000 + +# 然后在 Gateway 配置中使用 vllm-chat 模型 +``` + +## 运行测试 + +```bash +# 单元测试 +go test ./internal/... -count=1 + +# 集成测试 +go test ./test/integration/ -count=1 + +# 全部测试 +go test ./internal/... -count=1 && \ +go test ./test/integration/ -count=1 && \ +go test ./test/security/ -count=1 && \ +go test ./test/compatibility/ -count=1 && \ +go test ./test/performance/ -count=1 && \ +go test ./test/chaos/ -count=1 +```