Resolve merge conflicts: keep origin/main version across 4 files

- backend-integration/README.md: Add Persona Studio API endpoints docs
- backend-integration/api-proxy.js: Add PS backend reverse proxy implementation
- persona-studio/frontend/chat.js: Use backend proxy instead of direct fetch
- persona-telemetry/latest-summary.json: Update timestamps and coverage format

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-03-11 07:45:36 +00:00
parent 1d48cad804
commit 33fc76a26e
4 changed files with 0 additions and 116 deletions

View File

@ -33,14 +33,11 @@ curl http://localhost:3721/api/models
| GET | `/api/health` | 健康检查 | | GET | `/api/health` | 健康检查 |
| POST | `/api/ps/apikey/detect-models` | 用户 API Key 模型检测Persona Studio | | POST | `/api/ps/apikey/detect-models` | 用户 API Key 模型检测Persona Studio |
| POST | `/api/ps/apikey/chat` | 用户 API Key 对话Persona Studio | | POST | `/api/ps/apikey/chat` | 用户 API Key 对话Persona Studio |
<<<<<<< HEAD
=======
| POST | `/api/ps/chat/message` | 知秋对话(→ PS 后端 port 3002 | | POST | `/api/ps/chat/message` | 知秋对话(→ PS 后端 port 3002 |
| GET | `/api/ps/chat/history` | 对话历史(→ PS 后端 port 3002 | | GET | `/api/ps/chat/history` | 对话历史(→ PS 后端 port 3002 |
| POST | `/api/ps/auth/login` | 开发编号登录(→ PS 后端 port 3002 | | POST | `/api/ps/auth/login` | 开发编号登录(→ PS 后端 port 3002 |
| POST | `/api/ps/build/start` | 开发任务触发(→ PS 后端 port 3002 | | POST | `/api/ps/build/start` | 开发任务触发(→ PS 后端 port 3002 |
| POST | `/api/ps/notify/send` | 邮件通知(→ PS 后端 port 3002 | | POST | `/api/ps/notify/send` | 邮件通知(→ PS 后端 port 3002 |
>>>>>>> origin/main
### 环境变量 ### 环境变量
@ -53,10 +50,7 @@ curl http://localhost:3721/api/models
| `OPENAI_API_KEY` | OpenAI 密钥(需海外服务器) | | `OPENAI_API_KEY` | OpenAI 密钥(需海外服务器) |
| `GEMINI_API_KEY` | Google Gemini 密钥(需海外服务器) | | `GEMINI_API_KEY` | Google Gemini 密钥(需海外服务器) |
| `PROXY_PORT` | 端口(默认 3721 | | `PROXY_PORT` | 端口(默认 3721 |
<<<<<<< HEAD
=======
| `PS_PORT` | Persona Studio 后端端口(默认 3002反向代理目标 | | `PS_PORT` | Persona Studio 后端端口(默认 3002反向代理目标 |
>>>>>>> origin/main
| `RATE_LIMIT_RPM` | 频率限制(默认 10 次/分钟) | | `RATE_LIMIT_RPM` | 频率限制(默认 10 次/分钟) |
### Nginx 配置 ### Nginx 配置

View File

@ -710,8 +710,6 @@ async function handleApiKeyChat(req, res) {
} }
// ═══════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════
<<<<<<< HEAD
=======
// Persona Studio 后端反向代理 // Persona Studio 后端反向代理
// 将 /api/ps/chat/*, /api/ps/auth/*, /api/ps/build/*, /api/ps/notify/* // 将 /api/ps/chat/*, /api/ps/auth/*, /api/ps/build/*, /api/ps/notify/*
// 转发到 persona-studio 后端服务(默认端口 3002 // 转发到 persona-studio 后端服务(默认端口 3002
@ -782,7 +780,6 @@ function proxyToPersonaStudio(req, res, fullPath) {
} }
// ═══════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════
>>>>>>> origin/main
// HTTP 服务器 // HTTP 服务器
// ═══════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════
const server = http.createServer(async (req, res) => { const server = http.createServer(async (req, res) => {
@ -810,20 +807,13 @@ const server = http.createServer(async (req, res) => {
await handleDetectModels(req, res); await handleDetectModels(req, res);
} else if (path === '/api/ps/apikey/chat' && req.method === 'POST') { } else if (path === '/api/ps/apikey/chat' && req.method === 'POST') {
await handleApiKeyChat(req, res); await handleApiKeyChat(req, res);
<<<<<<< HEAD
=======
} else if (shouldProxyToPersonaStudio(path)) { } else if (shouldProxyToPersonaStudio(path)) {
await proxyToPersonaStudio(req, res, url.pathname + url.search); await proxyToPersonaStudio(req, res, url.pathname + url.search);
>>>>>>> origin/main
} else { } else {
jsonResponse(res, 404, { jsonResponse(res, 404, {
error: true, error: true,
code: 'NOT_FOUND', code: 'NOT_FOUND',
<<<<<<< HEAD
message: '接口不存在。可用接口: POST /api/chat, GET /api/models, GET /api/health, POST /api/ps/apikey/detect-models, POST /api/ps/apikey/chat'
=======
message: '接口不存在。可用接口: POST /api/chat, GET /api/models, GET /api/health, POST /api/ps/apikey/detect-models, POST /api/ps/apikey/chat, POST /api/ps/chat/message, GET /api/ps/chat/history, POST /api/ps/auth/login' message: '接口不存在。可用接口: POST /api/chat, GET /api/models, GET /api/health, POST /api/ps/apikey/detect-models, POST /api/ps/apikey/chat, POST /api/ps/chat/message, GET /api/ps/chat/history, POST /api/ps/auth/login'
>>>>>>> origin/main
}); });
} }
} catch (err) { } catch (err) {
@ -863,23 +853,17 @@ server.listen(PORT, () => {
console.log(' export YUNWU_API_KEY=sk-xxx'); console.log(' export YUNWU_API_KEY=sk-xxx');
} }
<<<<<<< HEAD
=======
console.log(`\n Persona Studio 后端代理: http://127.0.0.1:${PS_BACKEND_PORT}`); console.log(`\n Persona Studio 后端代理: http://127.0.0.1:${PS_BACKEND_PORT}`);
console.log(' 代理路径: /api/ps/chat/*, /api/ps/auth/*, /api/ps/build/*, /api/ps/notify/*'); console.log(' 代理路径: /api/ps/chat/*, /api/ps/auth/*, /api/ps/build/*, /api/ps/notify/*');
>>>>>>> origin/main
console.log('\n 可用接口:'); console.log('\n 可用接口:');
console.log(' POST /api/chat — 聊天代理SSE 流式)'); console.log(' POST /api/chat — 聊天代理SSE 流式)');
console.log(' GET /api/models — 列出可用模型'); console.log(' GET /api/models — 列出可用模型');
console.log(' GET /api/health — 健康检查'); console.log(' GET /api/health — 健康检查');
console.log(' POST /api/ps/apikey/detect-models — 用户 API Key 模型检测'); console.log(' POST /api/ps/apikey/detect-models — 用户 API Key 模型检测');
console.log(' POST /api/ps/apikey/chat — 用户 API Key 对话'); console.log(' POST /api/ps/apikey/chat — 用户 API Key 对话');
<<<<<<< HEAD
=======
console.log(' POST /api/ps/chat/message — 知秋对话(→ PS后端'); console.log(' POST /api/ps/chat/message — 知秋对话(→ PS后端');
console.log(' GET /api/ps/chat/history — 对话历史(→ PS后端'); console.log(' GET /api/ps/chat/history — 对话历史(→ PS后端');
console.log(' POST /api/ps/auth/login — 登录校验(→ PS后端'); console.log(' POST /api/ps/auth/login — 登录校验(→ PS后端');
>>>>>>> origin/main
console.log(''); console.log('');
}); });

View File

@ -140,93 +140,12 @@ async function sendMessage() {
input.focus(); input.focus();
} }
<<<<<<< HEAD
/* ---- API Key 流式响应(浏览器直连) ---- */
=======
/* ---- API Key 对话(通过后端代理,避免 CORS 问题) ---- */ /* ---- API Key 对话(通过后端代理,避免 CORS 问题) ---- */
>>>>>>> origin/main
async function streamApiKeyReply(text) { async function streamApiKeyReply(text) {
var apiMessages = conversationHistory.slice(-20).map(function (msg) { var apiMessages = conversationHistory.slice(-20).map(function (msg) {
return { role: msg.role === 'assistant' ? 'assistant' : 'user', content: msg.content }; return { role: msg.role === 'assistant' ? 'assistant' : 'user', content: msg.content };
}); });
<<<<<<< HEAD
var chatUrl = USER_API_BASE + '/chat/completions';
var reqBody = {
model: SELECTED_MODEL,
messages: apiMessages,
stream: true,
max_tokens: 2000,
temperature: 0.8
};
var res = await fetch(chatUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + USER_API_KEY
},
body: JSON.stringify(reqBody)
});
if (!res.ok) {
var errText = '请求失败 (HTTP ' + res.status + ')';
try {
var errData = await res.json();
errText = errData.error?.message || errText;
} catch (_e) { /* ignore parse error */ }
appendMessage('system', '⚠️ ' + errText);
return;
}
// 流式读取响应
var streamEl = appendStreamMessage();
var full = '';
var reader = res.body.getReader();
var decoder = new TextDecoder();
var buf = '';
while (true) {
var chunk = await reader.read();
if (chunk.done) break;
buf += decoder.decode(chunk.value, { stream: true });
var lines = buf.split('\n');
buf = lines.pop();
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
if (!line.startsWith('data: ')) continue;
var d = line.slice(6);
if (d === '[DONE]') continue;
try {
var parsed = JSON.parse(d);
var delta = parsed.choices && parsed.choices[0]
&& parsed.choices[0].delta
&& parsed.choices[0].delta.content;
if (delta) {
full += delta;
streamEl.textContent = full;
}
} catch (_e) { /* ignore parse error */ }
}
}
// 如果没有流式内容,尝试非流式解析
if (!full && buf) {
try {
var jsonRes = JSON.parse(buf);
if (jsonRes.choices && jsonRes.choices[0] && jsonRes.choices[0].message) {
full = jsonRes.choices[0].message.content;
streamEl.textContent = full;
}
} catch (_e) { /* ignore */ }
}
if (full) {
conversationHistory.push({ role: 'assistant', content: full });
} else {
streamEl.textContent = '(未收到有效回复)';
=======
var streamEl = appendStreamMessage(); var streamEl = appendStreamMessage();
try { try {
@ -261,7 +180,6 @@ async function streamApiKeyReply(text) {
} }
} catch (err) { } catch (err) {
streamEl.textContent = '⚠️ ' + (err.message || '请求失败,请检查网络连接'); streamEl.textContent = '⚠️ ' + (err.message || '请求失败,请检查网络连接');
>>>>>>> origin/main
} }
} }

View File

@ -1,10 +1,6 @@
{ {
"version": "1.0", "version": "1.0",
<<<<<<< HEAD
"timestamp": "2026-03-09T23:59:23.782Z",
=======
"timestamp": "2026-03-10T23:59:09.654Z", "timestamp": "2026-03-10T23:59:09.654Z",
>>>>>>> origin/main
"sessions": { "sessions": {
"total_24h": 0, "total_24h": 0,
"active_devs": [ "active_devs": [
@ -23,11 +19,7 @@
"style_profile": "通感语言·守护者", "style_profile": "通感语言·守护者",
"style_drift_score": 0, "style_drift_score": 0,
"memory_depth": "0 selfchecks", "memory_depth": "0 selfchecks",
<<<<<<< HEAD
"last_brain_update": "2026-03-09T23:59:23.782Z"
=======
"last_brain_update": "2026-03-10T13:24:33.728Z" "last_brain_update": "2026-03-10T13:24:33.728Z"
>>>>>>> origin/main
}, },
"dev_progress": { "dev_progress": {
"syslog_submitted": 0, "syslog_submitted": 0,
@ -40,11 +32,7 @@
"next_scheduled": null "next_scheduled": null
}, },
"repo_health": { "repo_health": {
<<<<<<< HEAD
"hli_coverage": "17.6%",
=======
"hli_coverage": "3/17", "hli_coverage": "3/17",
>>>>>>> origin/main
"total_team_members": 9, "total_team_members": 9,
"green_status_count": 6 "green_status_count": 6
}, },