dsh-neartoken 0.16.0 → 0.16.2
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/README.md +2 -1
- package/lib/client.js +98 -98
- package/lib/index.js +95 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -204,6 +204,7 @@ Gemini 系走原生 surface 而不是 openai 兼容层:同模型同提示词
|
|
|
204
204
|
## 已知限制
|
|
205
205
|
|
|
206
206
|
- 四套 wire(chat / anthropic / genai / responses)各自解析 SSE;**图片按各协议发送** —— chat 用 `image_url` part、anthropic 用 `source.base64`、genai 用 `inlineData`、responses 用 `input_image`。发送前先按 `imageMaxEdge` / `imageMaxBytes` 重编码(见「图片压缩」),所以线上字节与媒体类型未必等于附件里的原图。**工具结果里的图片也发**(`read_image` 的结果就是一段文本信封 + 一张图):anthropic 把图塞回同一个 `tool_result` 的 content 块数组;genai 在同一 user turn 的 parts 里补 `inlineData`;chat 的 tool 消息只能放字符串、responses 的 `function_call_output` 同理,所以图跟在**整段连续工具结果之后**的一条 user 消息 / 条目里(并行调用时不会把 tool 消息切断)。字节由 `attachments.readImage` 在请求前内联;没有 attachments 服务或读不出来会**让请求失败**,不再静默降级成纯文本(曾经就是这样:贴的截图模型根本收不到)。会话里被 image-offload 标记的图片(`offloaded: true`)不发字节;插件也不替它生成占位文本,所以那一次图片对模型完全不可见。文件块(非图片附件)仍不发;`/model` 弹窗仍是内置平铺列表;箭头键盘导航未接。
|
|
207
|
+
- **genai 线的并行工具调用必须收在同一个 user turn 里**:Gemini 校验「一个 functionCall turn ↔ 紧随其后那条 user turn 里等量的 functionResponse parts」,每条工具结果各开一条 user turn 会被上游拒成 400 `Please ensure that the number of function response parts is equal to the number of function call parts of the function call turn`。它是**确定性**错误:历史不变就必然复现,重试、换分组都无用,只能新开会话(0.16.1 及以前踩中过一次:一轮发了 6 个并行 `read`,整轮就此作废)。0.16.2 起把整段连续的工具结果折进同一个 user turn;Gemini ≥ 3 另给 `functionCall` / `functionResponse` 带上 `id`(同一个函数被并行调多次时才分得清谁答谁),`gemma*` 与更早的 Gemini 不发该字段,只按函数名匹配。
|
|
207
208
|
- 四套 wire 的 SSE 解析都有「帧样本 → 期望 chunk 序列」回归测试(`src/host/wire-parse.spec.ts`),帧样本取自生产网关的真实响应。网关在内容帧上发 `"usage": null`,只有末帧带真值:任何一处把 `usage` 当对象直接读都会让整条流中途崩掉(曾导致 claude 模型全不可用)。
|
|
208
209
|
- **每个请求都带会话亲和键**:chat 与 responses 发 `prompt_cache_key`,anthropic 发 `metadata.user_id`,取值都是 DSH 的 `GenerateOptions.sessionId`。站点就是靠这个值挑上游渠道的(New API 的「codex cli trace」规则:`^gpt-.*$` + `/v1/responses`,键取自 body 的 `prompt_cache_key`,取不到就把整条规则跳过),而提示词缓存按**上游账号**存 —— 不带键时每个请求随机换账号,缓存永远不中。实测同一 6k 前缀:不带键连发 4 次 `cached_tokens` 全 0;带键后第 2 次起 5888/6011 ≈ 98%。`/v1/chat/completions` 上目前没有对应规则,chat 线即使带键也只是偶发命中,要彻底解决需要站点加一条匹配该路径的亲和规则;anthropic 线的 `metadata.user_id` 同理(规则只作用于 `/v1/messages`,这一条已经匹配)。
|
|
209
210
|
- **anthropic 线显式打缓存断点**:Anthropic 只在 `cache_control` 断点处写缓存,没有断点就整段 prompt 按新输入计价(亲和键只保证落在同一上游账号,不产生缓存)。每个请求打 4 个断点:`tools` 末项、`system` 末块、最后两条消息各自末块 —— harness 每轮全量重放历史,尾部双断点保证下一轮几乎全命中,每轮只写新增片段。实测(Claude Max 超惠分组,~6.3k token 前缀):无断点连发两次 `cache_read` 全 0;打点后第 2 次起 `in=4, cr=6316`,三轮增长历史 `cr` 累积到 10811、`in` 恒为个位数。断点也覆盖 `tool_result` 结尾的轮次(DSH 会话的常态形态)。
|
|
@@ -215,7 +216,7 @@ Gemini 系走原生 surface 而不是 openai 兼容层:同模型同提示词
|
|
|
215
216
|
- `-c` 后缀是站点自己的约定(带该后缀的模型工具调用不可靠):站点声明了端点类型时插件按声明提供,声明里没有它就不会出现;没有声明时按名字隐藏它的无后缀孪生之外的变体。
|
|
216
217
|
- 站点上新但还没在后台填能力的模型:只要声明了端点类型就会提供,容量走 `defaultContextWindow`,模态按纯文本、没有推理档位(除非在 `modelOverrides` 里写明)。这不是 bug,是「站点是唯一真源」的直接后果——补能力请去后台填,不必等插件发版。
|
|
217
218
|
- pricing 加载失败时沿用上一次成功的结果,从未成功过则目录为空(失败不写缓存,下次调用会重试)。
|
|
218
|
-
- **上游错误映射到共享重试码表**(`src/host/wire-error.ts`):429→`RATE_LIMIT`、5xx/529/流中途失败→`SERVER`、网络层断连→`TRANSPORT`(fetch 抛错但非用户取消),这三类是 base 组合里 `dsh-llm-retry` 默认策略(5 次、指数退避、读 `retry-after`)会等过去重试的;401/403→`AUTH`、402/余额耗尽→`QUOTA`、400/413→`INVALID_REQUEST`、上下文超限→`CONTEXT_WINDOW_EXCEEDED`、其余→`HTTP_<status>` 保持不可重试。曾经全部抛 `PROVIDER_ERROR
|
|
219
|
+
- **上游错误映射到共享重试码表**(`src/host/wire-error.ts`):429→`RATE_LIMIT`、5xx/529/流中途失败→`SERVER`、网络层断连→`TRANSPORT`(fetch 抛错但非用户取消),这三类是 base 组合里 `dsh-llm-retry` 默认策略(5 次、指数退避、读 `retry-after`)会等过去重试的;401/403→`AUTH`、402/余额耗尽→`QUOTA`、400/413→`INVALID_REQUEST`、上下文超限→`CONTEXT_WINDOW_EXCEEDED`、其余→`HTTP_<status>` 保持不可重试。曾经全部抛 `PROVIDER_ERROR`——不在默认可重试集合里,网关一次瞬断就终止整轮(「一遇错误立即停止」的根因)。识别到上面那句 function response 计数对不上的文案时,错误消息里会直接带一句中文处置提示(码仍是 `INVALID_REQUEST`,不进重试集合)。
|
|
219
220
|
- 账号卡片的钱是**两次读**拼接的:授权那一刻 grant 里的 `user` 给出身份事实,余额/已用/代金券则来自站点 `/api/dsh/device/wallet`——Host 每 5 分钟读一次,页面在余额旁另有**立即刷新**按钮(`POST /neartoken/refresh`,读完后把同一份状态回给页面),所以不必重连也能看到新数字。落盘到 profile 下的 `neartoken-account.json` 只作重启回退。站点若没有该端点或读不到,卡片保留上一次的数字、不会清空。代金券为空时卡片给一句去「邀请计划」(站点 `/wallet` 页)领免费代金券的提示。
|
|
220
221
|
- 白名单按裸模型名过滤:勾掉某模型会把它的所有分组一起隐藏;某个正在使用的模型被勾掉后,会话里已选中的 id 仍在,但选择器里不再列出它。
|
|
221
222
|
- 白名单写入走 DSH 的 Settings:profile 里该行只保留 volatile 字段,所以别把 `baseURL` 等非 volatile 字段写进**同一个 profile 行**(会在此后第一次保存白名单时被 Settings 的 strip 规则清掉)。
|
package/lib/client.js
CHANGED
|
@@ -401,23 +401,23 @@ window.__ModuleLoader__.load({
|
|
|
401
401
|
document.head.appendChild(tag);
|
|
402
402
|
}
|
|
403
403
|
var group_tags_module_css_default = {
|
|
404
|
-
"toneCyan": "FotuUq_toneCyan",
|
|
405
|
-
"tonePink": "FotuUq_tonePink",
|
|
406
|
-
"toneSky": "FotuUq_toneSky",
|
|
407
404
|
"toneAmber": "FotuUq_toneAmber",
|
|
408
|
-
"toneLime": "FotuUq_toneLime",
|
|
409
|
-
"toneRose": "FotuUq_toneRose",
|
|
410
405
|
"toneOrange": "FotuUq_toneOrange",
|
|
411
|
-
"
|
|
412
|
-
"
|
|
406
|
+
"toneEmerald": "FotuUq_toneEmerald",
|
|
407
|
+
"tonePink": "FotuUq_tonePink",
|
|
408
|
+
"toneCyan": "FotuUq_toneCyan",
|
|
409
|
+
"ratioChip": "FotuUq_ratioChip",
|
|
410
|
+
"toneLime": "FotuUq_toneLime",
|
|
413
411
|
"toneTeal": "FotuUq_toneTeal",
|
|
414
|
-
"
|
|
415
|
-
"toneIndigo": "FotuUq_toneIndigo",
|
|
416
|
-
"ratioCheap": "FotuUq_ratioCheap",
|
|
412
|
+
"toneSky": "FotuUq_toneSky",
|
|
417
413
|
"tag": "FotuUq_tag",
|
|
418
|
-
"
|
|
419
|
-
"
|
|
420
|
-
"
|
|
414
|
+
"ratioCheap": "FotuUq_ratioCheap",
|
|
415
|
+
"toneFuchsia": "FotuUq_toneFuchsia",
|
|
416
|
+
"ratioPricey": "FotuUq_ratioPricey",
|
|
417
|
+
"ratioBase": "FotuUq_ratioBase",
|
|
418
|
+
"toneRose": "FotuUq_toneRose",
|
|
419
|
+
"toneViolet": "FotuUq_toneViolet",
|
|
420
|
+
"toneIndigo": "FotuUq_toneIndigo"
|
|
421
421
|
};
|
|
422
422
|
//#endregion
|
|
423
423
|
//#region src/client/group-tags.tsx
|
|
@@ -472,39 +472,39 @@ window.__ModuleLoader__.load({
|
|
|
472
472
|
document.head.appendChild(tag);
|
|
473
473
|
}
|
|
474
474
|
var ModelGroupSelect_module_css_default = {
|
|
475
|
-
"
|
|
476
|
-
"
|
|
477
|
-
"
|
|
475
|
+
"selected": "_2pt-bW_selected",
|
|
476
|
+
"optionTrailing": "_2pt-bW_optionTrailing",
|
|
477
|
+
"searchRow": "_2pt-bW_searchRow",
|
|
478
|
+
"retry": "_2pt-bW_retry",
|
|
479
|
+
"variantCount": "_2pt-bW_variantCount",
|
|
480
|
+
"cellLabel": "_2pt-bW_cellLabel",
|
|
478
481
|
"error": "_2pt-bW_error",
|
|
479
|
-
"chevron": "_2pt-bW_chevron",
|
|
480
|
-
"groupInline": "_2pt-bW_groupInline",
|
|
481
|
-
"empty": "_2pt-bW_empty",
|
|
482
|
-
"cellChevron": "_2pt-bW_cellChevron",
|
|
483
482
|
"searchClear": "_2pt-bW_searchClear",
|
|
484
|
-
"
|
|
483
|
+
"check": "_2pt-bW_check",
|
|
484
|
+
"triggerEffort": "_2pt-bW_triggerEffort",
|
|
485
|
+
"modelOption": "_2pt-bW_modelOption",
|
|
486
|
+
"optionCopy": "_2pt-bW_optionCopy",
|
|
487
|
+
"chevron": "_2pt-bW_chevron",
|
|
488
|
+
"groupOptionLabel": "_2pt-bW_groupOptionLabel",
|
|
489
|
+
"toast": "_2pt-bW_toast",
|
|
485
490
|
"triggerLabel": "_2pt-bW_triggerLabel",
|
|
486
|
-
"
|
|
491
|
+
"menu": "_2pt-bW_menu",
|
|
492
|
+
"cell": "_2pt-bW_cell",
|
|
493
|
+
"cellChevron": "_2pt-bW_cellChevron",
|
|
494
|
+
"status": "_2pt-bW_status",
|
|
495
|
+
"empty": "_2pt-bW_empty",
|
|
487
496
|
"search": "_2pt-bW_search",
|
|
488
|
-
"
|
|
489
|
-
"groupName": "_2pt-bW_groupName",
|
|
490
|
-
"optionCopy": "_2pt-bW_optionCopy",
|
|
497
|
+
"trigger": "_2pt-bW_trigger",
|
|
491
498
|
"warning": "_2pt-bW_warning",
|
|
492
|
-
"
|
|
499
|
+
"modelName": "_2pt-bW_modelName",
|
|
500
|
+
"groups": "_2pt-bW_groups",
|
|
501
|
+
"cellValue": "_2pt-bW_cellValue",
|
|
493
502
|
"triggerIcon": "_2pt-bW_triggerIcon",
|
|
494
|
-
"
|
|
495
|
-
"retry": "_2pt-bW_retry",
|
|
496
|
-
"variantCount": "_2pt-bW_variantCount",
|
|
497
|
-
"cell": "_2pt-bW_cell",
|
|
503
|
+
"optionActive": "_2pt-bW_optionActive",
|
|
498
504
|
"root": "_2pt-bW_root",
|
|
499
|
-
"
|
|
500
|
-
"
|
|
501
|
-
"
|
|
502
|
-
"toast": "_2pt-bW_toast",
|
|
503
|
-
"selected": "_2pt-bW_selected",
|
|
504
|
-
"check": "_2pt-bW_check",
|
|
505
|
-
"cellLabel": "_2pt-bW_cellLabel",
|
|
506
|
-
"searchRow": "_2pt-bW_searchRow",
|
|
507
|
-
"optionActive": "_2pt-bW_optionActive"
|
|
505
|
+
"groupName": "_2pt-bW_groupName",
|
|
506
|
+
"groupInline": "_2pt-bW_groupInline",
|
|
507
|
+
"option": "_2pt-bW_option"
|
|
508
508
|
};
|
|
509
509
|
//#endregion
|
|
510
510
|
//#region src/client/ModelGroupSelect.tsx
|
|
@@ -1070,77 +1070,77 @@ window.__ModuleLoader__.load({
|
|
|
1070
1070
|
document.head.appendChild(tag);
|
|
1071
1071
|
}
|
|
1072
1072
|
var NearTokenSection_module_css_default = {
|
|
1073
|
-
"
|
|
1074
|
-
"
|
|
1075
|
-
"
|
|
1076
|
-
"
|
|
1077
|
-
"
|
|
1078
|
-
"factLabel": "_02HI2q_factLabel",
|
|
1079
|
-
"topupChipActive": "_02HI2q_topupChipActive",
|
|
1080
|
-
"hint": "_02HI2q_hint",
|
|
1081
|
-
"rowName": "_02HI2q_rowName",
|
|
1082
|
-
"tipGroupName": "_02HI2q_tipGroupName",
|
|
1083
|
-
"name": "_02HI2q_name",
|
|
1073
|
+
"metaCheck": "_02HI2q_metaCheck",
|
|
1074
|
+
"fact": "_02HI2q_fact",
|
|
1075
|
+
"linkRow": "_02HI2q_linkRow",
|
|
1076
|
+
"search": "_02HI2q_search",
|
|
1077
|
+
"metaHint": "_02HI2q_metaHint",
|
|
1084
1078
|
"facts": "_02HI2q_facts",
|
|
1085
|
-
"
|
|
1079
|
+
"label": "_02HI2q_label",
|
|
1080
|
+
"button": "_02HI2q_button",
|
|
1081
|
+
"metaIntro": "_02HI2q_metaIntro",
|
|
1082
|
+
"card": "_02HI2q_card",
|
|
1083
|
+
"topupInput": "_02HI2q_topupInput",
|
|
1084
|
+
"cardHead": "_02HI2q_cardHead",
|
|
1085
|
+
"section": "_02HI2q_section",
|
|
1086
|
+
"valueRow": "_02HI2q_valueRow",
|
|
1087
|
+
"topupChips": "_02HI2q_topupChips",
|
|
1088
|
+
"toolbar": "_02HI2q_toolbar",
|
|
1089
|
+
"rowName": "_02HI2q_rowName",
|
|
1086
1090
|
"count": "_02HI2q_count",
|
|
1087
|
-
"
|
|
1088
|
-
"
|
|
1091
|
+
"voucherList": "_02HI2q_voucherList",
|
|
1092
|
+
"actions": "_02HI2q_actions",
|
|
1093
|
+
"hint": "_02HI2q_hint",
|
|
1089
1094
|
"rowMeta": "_02HI2q_rowMeta",
|
|
1090
|
-
"
|
|
1091
|
-
"
|
|
1092
|
-
"voucherGroup": "_02HI2q_voucherGroup",
|
|
1095
|
+
"voucherFact": "_02HI2q_voucherFact",
|
|
1096
|
+
"metaBtnOn": "_02HI2q_metaBtnOn",
|
|
1093
1097
|
"linkLine": "_02HI2q_linkLine",
|
|
1094
|
-
"
|
|
1098
|
+
"link": "_02HI2q_link",
|
|
1099
|
+
"metaLabel": "_02HI2q_metaLabel",
|
|
1100
|
+
"handle": "_02HI2q_handle",
|
|
1101
|
+
"voucherEmpty": "_02HI2q_voucherEmpty",
|
|
1102
|
+
"code": "_02HI2q_code",
|
|
1103
|
+
"list": "_02HI2q_list",
|
|
1095
1104
|
"chevronOpen": "_02HI2q_chevronOpen",
|
|
1096
1105
|
"voucherAmount": "_02HI2q_voucherAmount",
|
|
1097
|
-
"
|
|
1106
|
+
"topupBar": "_02HI2q_topupBar",
|
|
1107
|
+
"tipTitle": "_02HI2q_tipTitle",
|
|
1108
|
+
"metaField": "_02HI2q_metaField",
|
|
1109
|
+
"row": "_02HI2q_row",
|
|
1110
|
+
"countDot": "_02HI2q_countDot",
|
|
1111
|
+
"buttonPrimary": "_02HI2q_buttonPrimary",
|
|
1112
|
+
"tipGroup": "_02HI2q_tipGroup",
|
|
1113
|
+
"factValue": "_02HI2q_factValue",
|
|
1114
|
+
"invite": "_02HI2q_invite",
|
|
1098
1115
|
"badge": "_02HI2q_badge",
|
|
1099
|
-
"
|
|
1116
|
+
"topupChip": "_02HI2q_topupChip",
|
|
1100
1117
|
"select": "_02HI2q_select",
|
|
1101
|
-
"
|
|
1102
|
-
"
|
|
1103
|
-
"
|
|
1104
|
-
"link": "_02HI2q_link",
|
|
1105
|
-
"actions": "_02HI2q_actions",
|
|
1106
|
-
"metaTitle": "_02HI2q_metaTitle",
|
|
1107
|
-
"section": "_02HI2q_section",
|
|
1108
|
-
"codeRow": "_02HI2q_codeRow",
|
|
1118
|
+
"topupChipActive": "_02HI2q_topupChipActive",
|
|
1119
|
+
"metaInput": "_02HI2q_metaInput",
|
|
1120
|
+
"tip": "_02HI2q_tip",
|
|
1109
1121
|
"title": "_02HI2q_title",
|
|
1110
|
-
"badgeOn": "_02HI2q_badgeOn",
|
|
1111
|
-
"autosave": "_02HI2q_autosave",
|
|
1112
|
-
"topupChips": "_02HI2q_topupChips",
|
|
1113
|
-
"tipRow": "_02HI2q_tipRow",
|
|
1114
|
-
"metaHint": "_02HI2q_metaHint",
|
|
1115
|
-
"code": "_02HI2q_code",
|
|
1116
|
-
"tipTitle": "_02HI2q_tipTitle",
|
|
1117
|
-
"valueRow": "_02HI2q_valueRow",
|
|
1118
|
-
"voucherFact": "_02HI2q_voucherFact",
|
|
1119
|
-
"voucherList": "_02HI2q_voucherList",
|
|
1120
|
-
"linkRow": "_02HI2q_linkRow",
|
|
1121
|
-
"label": "_02HI2q_label",
|
|
1122
|
-
"metaCheck": "_02HI2q_metaCheck",
|
|
1123
|
-
"toolbar": "_02HI2q_toolbar",
|
|
1124
|
-
"topupInput": "_02HI2q_topupInput",
|
|
1125
|
-
"metaIntro": "_02HI2q_metaIntro",
|
|
1126
|
-
"fact": "_02HI2q_fact",
|
|
1127
|
-
"search": "_02HI2q_search",
|
|
1128
|
-
"list": "_02HI2q_list",
|
|
1129
|
-
"rowRatio": "_02HI2q_rowRatio",
|
|
1130
|
-
"card": "_02HI2q_card",
|
|
1131
1122
|
"metaActions": "_02HI2q_metaActions",
|
|
1132
|
-
"
|
|
1133
|
-
"metaLabel": "_02HI2q_metaLabel",
|
|
1134
|
-
"intro": "_02HI2q_intro",
|
|
1135
|
-
"button": "_02HI2q_button",
|
|
1136
|
-
"tipGroup": "_02HI2q_tipGroup",
|
|
1137
|
-
"tip": "_02HI2q_tip",
|
|
1138
|
-
"topupBar": "_02HI2q_topupBar",
|
|
1123
|
+
"autosave": "_02HI2q_autosave",
|
|
1139
1124
|
"voucherRow": "_02HI2q_voucherRow",
|
|
1140
|
-
"countPicked": "_02HI2q_countPicked",
|
|
1141
|
-
"metaField": "_02HI2q_metaField",
|
|
1142
1125
|
"notice": "_02HI2q_notice",
|
|
1143
|
-
"
|
|
1126
|
+
"intro": "_02HI2q_intro",
|
|
1127
|
+
"rowRatio": "_02HI2q_rowRatio",
|
|
1128
|
+
"form": "_02HI2q_form",
|
|
1129
|
+
"codeRow": "_02HI2q_codeRow",
|
|
1130
|
+
"refresh": "_02HI2q_refresh",
|
|
1131
|
+
"topup": "_02HI2q_topup",
|
|
1132
|
+
"tipGroupName": "_02HI2q_tipGroupName",
|
|
1133
|
+
"tipRow": "_02HI2q_tipRow",
|
|
1134
|
+
"metaEditor": "_02HI2q_metaEditor",
|
|
1135
|
+
"voucherGroup": "_02HI2q_voucherGroup",
|
|
1136
|
+
"metaTitle": "_02HI2q_metaTitle",
|
|
1137
|
+
"countPicked": "_02HI2q_countPicked",
|
|
1138
|
+
"name": "_02HI2q_name",
|
|
1139
|
+
"cardToggle": "_02HI2q_cardToggle",
|
|
1140
|
+
"metaBtn": "_02HI2q_metaBtn",
|
|
1141
|
+
"factLabel": "_02HI2q_factLabel",
|
|
1142
|
+
"badgeOn": "_02HI2q_badgeOn",
|
|
1143
|
+
"chevron": "_02HI2q_chevron"
|
|
1144
1144
|
};
|
|
1145
1145
|
//#endregion
|
|
1146
1146
|
//#region src/client/NearTokenSection.tsx
|
package/lib/index.js
CHANGED
|
@@ -2016,6 +2016,17 @@ function parseJsonObject(payload) {
|
|
|
2016
2016
|
*
|
|
2017
2017
|
* @module
|
|
2018
2018
|
*/
|
|
2019
|
+
/**
|
|
2020
|
+
* Wording the gateway uses when a functionCall turn was not answered by the same
|
|
2021
|
+
* number of functionResponse parts.
|
|
2022
|
+
*
|
|
2023
|
+
* A Gemini batch lands here whenever its results were sent one user turn each;
|
|
2024
|
+
* no retry clears it, because the conversation that produced the refusal is
|
|
2025
|
+
* replayed unchanged.
|
|
2026
|
+
*/
|
|
2027
|
+
const FUNCTION_RESPONSE_MISMATCH = /number of function response parts/iu;
|
|
2028
|
+
/** What the user can do about a refusal the plugin itself cannot repair. */
|
|
2029
|
+
const FUNCTION_RESPONSE_MISMATCH_HINT = " 提示:Gemini 原生协议下这批工具结果与调用对不上,重试不会成功;请升级 neartoken 插件,或换用别的模型后新开会话";
|
|
2019
2030
|
/** The `error` object of a gateway error body, when the body carries one. */
|
|
2020
2031
|
function errorObjectOf(body) {
|
|
2021
2032
|
const parsed = parseJsonObject(body);
|
|
@@ -2077,7 +2088,8 @@ function gatewayError(body, status, headers) {
|
|
|
2077
2088
|
const retry = headers?.get("retry-after");
|
|
2078
2089
|
const delay = retry == null ? NaN : /^\d+(?:\.\d+)?$/u.test(retry) ? Number(retry) * 1e3 : Date.parse(retry) - Date.now();
|
|
2079
2090
|
const id = headers?.get("request-id") ?? headers?.get("x-request-id");
|
|
2080
|
-
|
|
2091
|
+
const hint = code === "INVALID_REQUEST" && FUNCTION_RESPONSE_MISMATCH.test(detail) ? FUNCTION_RESPONSE_MISMATCH_HINT : "";
|
|
2092
|
+
return new LlmError(`NearToken answered ${status}: ${(embeddedMessage(body) ?? body).slice(0, 500)}${hint}`, code, {
|
|
2081
2093
|
status,
|
|
2082
2094
|
...id == null ? {} : { requestId: ProviderRequestId(id) },
|
|
2083
2095
|
...Number.isFinite(delay) && delay > 0 ? { providerRetryAfterMs: delay } : {}
|
|
@@ -2303,6 +2315,12 @@ function dataUrlOf(image) {
|
|
|
2303
2315
|
* on every call, so the breakpoints are placed once per request over the bytes
|
|
2304
2316
|
* the next call will reuse (see `serializeRequest`).
|
|
2305
2317
|
*
|
|
2318
|
+
* The system prompt owns the `system` slot even when the query carries it as a
|
|
2319
|
+
* leading system-role message, which is how a loop-built request delivers it
|
|
2320
|
+
* (see `GenerateOptions`). Folded into the turn list instead, it would lose the
|
|
2321
|
+
* slot the model weights as system, and its breakpoint would be the moving tail
|
|
2322
|
+
* of the message list rather than a fixed end of the prompt prefix.
|
|
2323
|
+
*
|
|
2306
2324
|
* @module
|
|
2307
2325
|
*/
|
|
2308
2326
|
/**
|
|
@@ -2468,7 +2486,15 @@ function claudeEffort(effort) {
|
|
|
2468
2486
|
*/
|
|
2469
2487
|
function serializeRequest$3(options, model, images = /* @__PURE__ */ new Map()) {
|
|
2470
2488
|
const messages = [];
|
|
2489
|
+
let system = options.system;
|
|
2471
2490
|
for (const message of options.messages) {
|
|
2491
|
+
if (message.role === "system" && system === void 0) {
|
|
2492
|
+
const text = textOf$3(message.content);
|
|
2493
|
+
if (text !== "") {
|
|
2494
|
+
system = text;
|
|
2495
|
+
continue;
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2472
2498
|
const wire = serializeMessage$3(message, images);
|
|
2473
2499
|
if (wire !== void 0) messages.push(wire);
|
|
2474
2500
|
}
|
|
@@ -2488,9 +2514,9 @@ function serializeRequest$3(options, model, images = /* @__PURE__ */ new Map())
|
|
|
2488
2514
|
}
|
|
2489
2515
|
if (options.maxTokens !== void 0) body.max_tokens = options.maxTokens;
|
|
2490
2516
|
if (options.temperature !== void 0) body.temperature = options.temperature;
|
|
2491
|
-
if (
|
|
2517
|
+
if (system !== void 0 && system.length > 0) body.system = [{
|
|
2492
2518
|
type: "text",
|
|
2493
|
-
text:
|
|
2519
|
+
text: system,
|
|
2494
2520
|
cache_control: cacheBreakpoint()
|
|
2495
2521
|
}];
|
|
2496
2522
|
if (options.tools !== void 0 && options.tools.length > 0) {
|
|
@@ -2768,9 +2794,11 @@ function toolNamesOf(messages) {
|
|
|
2768
2794
|
* Map one harness message onto its genai form.
|
|
2769
2795
|
* @param message - one conversation message.
|
|
2770
2796
|
* @param toolNames - call id to function name, for tool results.
|
|
2797
|
+
* @param images - the request's inlined images.
|
|
2798
|
+
* @param callIds - whether the model matches a response to its call by id.
|
|
2771
2799
|
* @returns the wire content, or undefined when it carries nothing.
|
|
2772
2800
|
*/
|
|
2773
|
-
function serializeMessage$2(message, toolNames, images = /* @__PURE__ */ new Map()) {
|
|
2801
|
+
function serializeMessage$2(message, toolNames, images = /* @__PURE__ */ new Map(), callIds = false) {
|
|
2774
2802
|
const content = message.content;
|
|
2775
2803
|
switch (message.role) {
|
|
2776
2804
|
case "user":
|
|
@@ -2805,35 +2833,52 @@ function serializeMessage$2(message, toolNames, images = /* @__PURE__ */ new Map
|
|
|
2805
2833
|
} catch {
|
|
2806
2834
|
args = {};
|
|
2807
2835
|
}
|
|
2808
|
-
|
|
2836
|
+
const functionCall = {
|
|
2809
2837
|
name: call.name,
|
|
2810
2838
|
args
|
|
2811
|
-
}
|
|
2839
|
+
};
|
|
2840
|
+
if (callIds && call.id !== void 0) functionCall.id = String(call.id);
|
|
2841
|
+
parts.push({ functionCall });
|
|
2812
2842
|
}
|
|
2813
2843
|
return parts.length === 0 ? void 0 : {
|
|
2814
2844
|
role: "model",
|
|
2815
2845
|
parts
|
|
2816
2846
|
};
|
|
2817
2847
|
}
|
|
2818
|
-
case "tool": {
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
response: { result: text }
|
|
2823
|
-
} }];
|
|
2824
|
-
for (const image of imagesIn(content, images)) parts.push({ inlineData: {
|
|
2825
|
-
mimeType: image.mediaType,
|
|
2826
|
-
data: image.data
|
|
2827
|
-
} });
|
|
2828
|
-
return {
|
|
2829
|
-
role: "user",
|
|
2830
|
-
parts
|
|
2831
|
-
};
|
|
2832
|
-
}
|
|
2848
|
+
case "tool": return {
|
|
2849
|
+
role: "user",
|
|
2850
|
+
parts: toolPartsOf(message, toolNames, images, callIds)
|
|
2851
|
+
};
|
|
2833
2852
|
default: return;
|
|
2834
2853
|
}
|
|
2835
2854
|
}
|
|
2836
2855
|
/**
|
|
2856
|
+
* The parts one tool result contributes to the user turn that answers a call.
|
|
2857
|
+
*
|
|
2858
|
+
* A functionResponse carries JSON, not bytes: a picture a tool returned follows
|
|
2859
|
+
* as its own inlineData part in the same user turn.
|
|
2860
|
+
* @param message - the harness tool message.
|
|
2861
|
+
* @param toolNames - call id to function name.
|
|
2862
|
+
* @param images - the request's inlined images.
|
|
2863
|
+
* @param callIds - whether the model matches a response to its call by id.
|
|
2864
|
+
* @returns the parts, starting with the functionResponse.
|
|
2865
|
+
*/
|
|
2866
|
+
function toolPartsOf(message, toolNames, images, callIds) {
|
|
2867
|
+
const content = message.content;
|
|
2868
|
+
const callId = String(message.toolCallId);
|
|
2869
|
+
const functionResponse = {
|
|
2870
|
+
name: toolNames.get(callId) ?? callId,
|
|
2871
|
+
response: { result: textOf$2(content) }
|
|
2872
|
+
};
|
|
2873
|
+
if (callIds) functionResponse.id = callId;
|
|
2874
|
+
const parts = [{ functionResponse }];
|
|
2875
|
+
for (const image of imagesIn(content, images)) parts.push({ inlineData: {
|
|
2876
|
+
mimeType: image.mediaType,
|
|
2877
|
+
data: image.data
|
|
2878
|
+
} });
|
|
2879
|
+
return parts;
|
|
2880
|
+
}
|
|
2881
|
+
/**
|
|
2837
2882
|
* Thinking budget for one harness reasoning effort.
|
|
2838
2883
|
*
|
|
2839
2884
|
* genai takes a token budget rather than an effort name. Levels are only
|
|
@@ -2866,6 +2911,20 @@ function usesThinkingLevel(model) {
|
|
|
2866
2911
|
return major !== void 0 && Number(major) >= 4;
|
|
2867
2912
|
}
|
|
2868
2913
|
/**
|
|
2914
|
+
* Whether a model takes an id on its function calls and responses.
|
|
2915
|
+
*
|
|
2916
|
+
* Gemini 3 added `id` to both, which is what tells two calls of the same
|
|
2917
|
+
* function apart inside one batch; earlier generations match on the function
|
|
2918
|
+
* name alone and refuse a member they do not declare. Gemma carries no version
|
|
2919
|
+
* in its name and stays on the older form.
|
|
2920
|
+
* @param model - the bare model id.
|
|
2921
|
+
* @returns true for the generations that declare the field.
|
|
2922
|
+
*/
|
|
2923
|
+
function usesCallIds(model) {
|
|
2924
|
+
const major = /gemini-(\d+)/i.exec(model)?.[1];
|
|
2925
|
+
return major !== void 0 && Number(major) >= 3;
|
|
2926
|
+
}
|
|
2927
|
+
/**
|
|
2869
2928
|
* The `thinkingConfig` for one request, in the vocabulary the model speaks.
|
|
2870
2929
|
* @param model - the bare model id.
|
|
2871
2930
|
* @param effort - the harness reasoning effort, when one was chosen.
|
|
@@ -2892,9 +2951,23 @@ function thinkingConfigOf(model, effort) {
|
|
|
2892
2951
|
*/
|
|
2893
2952
|
function serializeRequest$2(options, model, images = /* @__PURE__ */ new Map()) {
|
|
2894
2953
|
const toolNames = toolNamesOf(options.messages);
|
|
2954
|
+
const callIds = usesCallIds(model);
|
|
2895
2955
|
const contents = [];
|
|
2956
|
+
let batch = null;
|
|
2896
2957
|
for (const message of options.messages) {
|
|
2897
|
-
|
|
2958
|
+
if (message.role === "tool") {
|
|
2959
|
+
if (batch === null) {
|
|
2960
|
+
batch = [];
|
|
2961
|
+
contents.push({
|
|
2962
|
+
role: "user",
|
|
2963
|
+
parts: batch
|
|
2964
|
+
});
|
|
2965
|
+
}
|
|
2966
|
+
batch.push(...toolPartsOf(message, toolNames, images, callIds));
|
|
2967
|
+
continue;
|
|
2968
|
+
}
|
|
2969
|
+
batch = null;
|
|
2970
|
+
const wire = serializeMessage$2(message, toolNames, images, callIds);
|
|
2898
2971
|
if (wire !== void 0) contents.push(wire);
|
|
2899
2972
|
}
|
|
2900
2973
|
const generationConfig = {};
|