ai_co_discuss 0.2.17 → 0.2.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/backend/server.py +3 -1
- package/docs/runner.md +3 -1
- package/package.json +1 -1
- package/runner/discussion_api.mjs +10 -0
- package/runner/discussion_launch.mjs +4 -1
- package/web/app.js +9 -3
- package/web/modern.css +1 -1
- package/web/modern.js +611 -548
package/backend/server.py
CHANGED
|
@@ -1442,7 +1442,7 @@ class DiscussionHandler(BaseHTTPRequestHandler):
|
|
|
1442
1442
|
"""Forward authenticated browser requests to the shared Node entrypoint and pin their website."""
|
|
1443
1443
|
site = self.local_request_site(method)
|
|
1444
1444
|
route = parts[2:]
|
|
1445
|
-
if method == "OPTIONS" and route in (["settings"], ["launch"], ["room-models"], ["room-member"], ["models"], ["api-key"], ["pick-directory"]):
|
|
1445
|
+
if method == "OPTIONS" and route in (["settings"], ["launch"], ["room-models"], ["room-member"], ["models"], ["api-key"], ["api-check"], ["pick-directory"]):
|
|
1446
1446
|
return self.respond({"ok": True})
|
|
1447
1447
|
if route == ["settings"] and method in ("GET", "POST"):
|
|
1448
1448
|
command = "settings"
|
|
@@ -1456,6 +1456,8 @@ class DiscussionHandler(BaseHTTPRequestHandler):
|
|
|
1456
1456
|
command = "models"
|
|
1457
1457
|
elif route == ["api-key"] and method == "POST":
|
|
1458
1458
|
command = "api-key"
|
|
1459
|
+
elif route == ["api-check"] and method == "POST":
|
|
1460
|
+
command = "api-check"
|
|
1459
1461
|
elif route == ["room-member"] and method == "POST":
|
|
1460
1462
|
command = "room-member"
|
|
1461
1463
|
elif route == ["pick-directory"] and method == "POST":
|
package/docs/runner.md
CHANGED
|
@@ -21,7 +21,9 @@
|
|
|
21
21
|
|
|
22
22
|
自定义地址保留完整路径前缀,填写到协议根路径,例如 [OpenRouter](https://openrouter.ai/docs/api_reference/overview) 的 `https://openrouter.ai/api/v1`,选择 Chat Completions 并填写其返回的 `provider/model` ID。程序分别追加 `/responses`、`/chat/completions`、`/messages` 或 `/models/{model}:generateContent`;不改写模型 ID 中的点号、横线或斜线,不按房间身份强制套用官方协议或思考参数。实现参考 [Cherry Studio 的供应商解析说明(固定版本)](https://github.com/CherryHQ/cherry-studio/blob/843a297fe73e9f527c8eb8291c0e1335f2279c04/docs/references/ai/provider-resolution.md) 对供应商、协议、Base URL 与 API 模型 ID 的拆分方式;协议转换独立实现,不引入其代码或 SDK。
|
|
23
23
|
|
|
24
|
-
`POST /api/local/api-key`(CLI 为 `codiscuss launch api-key --file FILE`)接收 `{base_url, api_key}`,仅向本机服务目录的 `api-keys/UUID.json` 写入,返回 `{api_key_ref}`。文件依赖本机用户目录权限保护,不作加密保险库;不要共享此目录。Base URL 改动后必须重新填写该地址对应的 Key。`launch settings` 读取结果附带 `api_keys`(`{Base URL: api_key_ref}`,同一地址取最近保存的一份,不含密钥),新建表单与改 Base URL 时自动带出该地址已保存的 Key
|
|
24
|
+
`POST /api/local/api-key`(CLI 为 `codiscuss launch api-key --file FILE`)接收 `{base_url, api_key}`,仅向本机服务目录的 `api-keys/UUID.json` 写入,返回 `{api_key_ref}`。文件依赖本机用户目录权限保护,不作加密保险库;不要共享此目录。Base URL 改动后必须重新填写该地址对应的 Key。`launch settings` 读取结果附带 `api_keys`(`{Base URL: api_key_ref}`,同一地址取最近保存的一份,不含密钥),新建表单与改 Base URL 时自动带出该地址已保存的 Key,无需重复填写。网页密钥输入框不设保存按钮,失焦或回车即保存,可切换明文显示;它是用 CSS 遮挡的文本框,避免浏览器提示生成或保存密码。输入仅在组件内存保留至保存成功,保存后清空输入框,只在占位文字显示末四位以示确认;密钥不进入控制器快照、localStorage、启动待办、房间数据或上游错误日志。
|
|
25
|
+
|
|
26
|
+
`POST /api/local/api-check`(CLI 为 `codiscuss launch api-check --file FILE`)接收 API profile,用该 profile 带一个空工具向模型发送一次极短请求(30 秒超时),一并核对地址、密钥、协议、模型与工具调用支持,成功返回 `{ok:true}`,失败只报 HTTP 状态。会产生一次极小的模型调用费用。网页 API 卡片背面的闪电按钮调用此入口。
|
|
25
27
|
|
|
26
28
|
启动配置中的 API profile 为 `{adapter:"api", base_url, api_protocol, api_key_ref, model}`;`api_protocol` 为 `responses/chat/anthropic/gemini`。启动器在私有房间配置保存 `api_home`,供服务解析密钥引用。API 不需要 executable、项目目录、会话 ID 或技能安装;API 会话编号是本机参与者绑定,不是厂商的聊天 ID。模型列表入口仍为 `launch models` / `/api/local/models`,传入此 profile,只返回模型元数据。
|
|
27
29
|
|
package/package.json
CHANGED
|
@@ -14,6 +14,8 @@ export const API_PROVIDERS = {
|
|
|
14
14
|
const PROTOCOLS = ['responses', 'chat', 'anthropic', 'gemini'];
|
|
15
15
|
const REQUEST_TIMEOUT_MS = 180_000;
|
|
16
16
|
const MAX_OUTPUT_TOKENS = 8192;
|
|
17
|
+
const CHECK_TIMEOUT_MS = 30_000;
|
|
18
|
+
const CHECK_TOOL = { name: 'noop', description: '连通性检查,无需调用', parameters: { type: 'object', properties: {} } };
|
|
17
19
|
|
|
18
20
|
/** Validate an explicit API root, keeping custom gateway paths intact. */
|
|
19
21
|
export function api_base_url(value) {
|
|
@@ -112,6 +114,14 @@ export async function api_models(home, profile) {
|
|
|
112
114
|
return { adapter: 'api', models, error: '', source: 'api' };
|
|
113
115
|
}
|
|
114
116
|
|
|
117
|
+
/** Send one tiny tool-enabled request so address, key, protocol and model are checked together. */
|
|
118
|
+
export async function api_check(home, profile) {
|
|
119
|
+
const key = read_api_key(home, profile);
|
|
120
|
+
const context = { system: '这是连通性检查。', history: api_history(profile, '只回复 OK。'), tools: [CHECK_TOOL] };
|
|
121
|
+
await api_generate(profile, key, context, AbortSignal.timeout(CHECK_TIMEOUT_MS));
|
|
122
|
+
return { ok: true };
|
|
123
|
+
}
|
|
124
|
+
|
|
115
125
|
/** Start protocol-specific history with a user request. */
|
|
116
126
|
export function api_history(profile, prompt) {
|
|
117
127
|
return profile.api_protocol === 'gemini' ? [{ role: 'user', parts: [{ text: prompt }] }] : [{ role: 'user', content: prompt }];
|
|
@@ -10,7 +10,7 @@ import { parseArgs } from 'node:util';
|
|
|
10
10
|
|
|
11
11
|
import { atomic_json } from './discussion_adapters.mjs';
|
|
12
12
|
import { software_models } from './discussion_models.mjs';
|
|
13
|
-
import { API_PROVIDERS, api_models, save_api_key, saved_api_keys, validate_api_profile } from './discussion_api.mjs';
|
|
13
|
+
import { API_PROVIDERS, api_check, api_models, save_api_key, saved_api_keys, validate_api_profile } from './discussion_api.mjs';
|
|
14
14
|
import { acquire_lock, api, CODEX_ADAPTERS, DiscussionStore, service_drafts, task_files, validate_config } from './discussion_runner.mjs';
|
|
15
15
|
import { assert_shared_home, CONNECTION_FIELDS, configuration_snapshot, read_registry, registered_identity, register_room, service_home, service_pid, start_service } from './discussion_service.mjs';
|
|
16
16
|
|
|
@@ -44,6 +44,7 @@ const HELP = `用法:codiscuss launch settings [--file FILE]
|
|
|
44
44
|
codiscuss launch room-models --file FILE
|
|
45
45
|
codiscuss launch models --file FILE
|
|
46
46
|
codiscuss launch api-key --file FILE
|
|
47
|
+
codiscuss launch api-check --file FILE
|
|
47
48
|
codiscuss launch room-member --file FILE
|
|
48
49
|
codiscuss launch pick-directory
|
|
49
50
|
|
|
@@ -53,6 +54,7 @@ room-config 修正已暂停房间的软件路径或切换 Codex App / CLI,不
|
|
|
53
54
|
room-models 只读指定 site、room_id 的本机登记模型与思考强度,不启动讨论。
|
|
54
55
|
models 按 adapter、executable 或 API profile 读取模型目录,不启动讨论或生成回答。
|
|
55
56
|
api-key 将 base_url、api_key 保存于本机私有目录,只返回 api_key_ref。
|
|
57
|
+
api-check 用 API profile 向模型发送一次极短请求,核对地址、密钥、协议与模型是否可用。
|
|
56
58
|
pick-directory 在本机弹出系统目录选择窗口,返回所选路径;取消时不返回路径。
|
|
57
59
|
room-member 为运行中的自由房间或讨论邀请、请回一位成员(讨论只接受本机唤醒的成员);须提供 site、room_id、token、action(add/rejoin)、name。
|
|
58
60
|
JSON 指定 site、entry(conversation/web)、room(建房内容,含 submission_id)
|
|
@@ -705,6 +707,7 @@ export async function launch_main(args = process.argv.slice(2), input_text) {
|
|
|
705
707
|
if (positionals[0] === 'settings') result = { ...(input ? save_launch_settings(home, input) : launch_settings(home)), api_providers: API_PROVIDERS,
|
|
706
708
|
api_keys: saved_api_keys(home) };
|
|
707
709
|
else if (positionals[0] === 'api-key' && input) result = save_api_key(home, input);
|
|
710
|
+
else if (positionals[0] === 'api-check' && input) result = await api_check(home, input);
|
|
708
711
|
else if (positionals[0] === 'room-models' && input) result = room_models(home, input);
|
|
709
712
|
else if (positionals[0] === 'models' && input?.adapter === 'api') result = await api_models(home, input);
|
|
710
713
|
else if (positionals[0] === 'models' && input) {
|
package/web/app.js
CHANGED
|
@@ -1437,8 +1437,8 @@ async function local_launch_request(action, payload) {
|
|
|
1437
1437
|
response = await fetch(`${local_connection?.endpoint || ""}/api/local/${action}`, {
|
|
1438
1438
|
method: payload === undefined ? "GET" : "POST", headers, credentials: "omit", redirect: "error",
|
|
1439
1439
|
body: payload === undefined ? undefined : JSON.stringify(payload),
|
|
1440
|
-
...(payload === undefined || ["room-models", "models"].includes(action)
|
|
1441
|
-
? { signal: AbortSignal.timeout(
|
|
1440
|
+
...(payload === undefined || ["room-models", "models", "api-check"].includes(action)
|
|
1441
|
+
? { signal: AbortSignal.timeout(["models", "api-check"].includes(action) ? MODEL_CATALOG_TIMEOUT_MS : LOCAL_CONNECTION_TIMEOUT_MS) } : {}) });
|
|
1442
1442
|
} catch (error) {
|
|
1443
1443
|
if (!local_connection) throw error;
|
|
1444
1444
|
throw new Error("无法连接本机服务:请在这台电脑运行 codiscuss local open,并允许浏览器访问本机网络,再重试原请求");
|
|
@@ -1623,6 +1623,12 @@ async function save_launch_api_key(name, api_key) {
|
|
|
1623
1623
|
publish_view();
|
|
1624
1624
|
}
|
|
1625
1625
|
|
|
1626
|
+
/** Ask the local service to send one tiny request with the current API profile; throws its reason on failure. */
|
|
1627
|
+
async function check_launch_api(name) {
|
|
1628
|
+
const { base_url, api_protocol, model, api_key_ref } = state.launch.profiles[name] || {};
|
|
1629
|
+
await local_launch_request("api-check", { adapter: "api", base_url, api_protocol, model, api_key_ref });
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1626
1632
|
/** Provide selectable software results without turning an unavailable list into a text field. */
|
|
1627
1633
|
function launch_model_state(name, launch = state.launch) {
|
|
1628
1634
|
const profile = launch.profiles[name] || {};
|
|
@@ -2983,7 +2989,7 @@ function change_view_theme(theme) {
|
|
|
2983
2989
|
|
|
2984
2990
|
window.discuss_view = { snapshot: view_snapshot, write_field: write_view_field,
|
|
2985
2991
|
write_launch: write_launch_field, write_room_connection, write_room_adapter, launch_clients: LAUNCH_CLIENTS,
|
|
2986
|
-
launch_model_state, load_launch_models, write_launch_model, save_launch_api_key, launch_summary, launch_can_set_access, launch_can_bind_session, pick_launch_directory, restore_launch_profile, reset_create_form, empty_discussion_message,
|
|
2992
|
+
launch_model_state, load_launch_models, write_launch_model, save_launch_api_key, check_launch_api, launch_summary, launch_can_set_access, launch_can_bind_session, pick_launch_directory, restore_launch_profile, reset_create_form, empty_discussion_message,
|
|
2987
2993
|
start_drag: start_directory_drag,
|
|
2988
2994
|
attachment_markup, attachment_size, attachment_file_icon, drop_attachments, paste_attachments,
|
|
2989
2995
|
write_expert: write_view_expert, add_instance: add_view_instance, platform_of, execute: execute_view_action, theme: change_view_theme,
|