@dan-ai-studio/dshopencodego 0.1.82 → 0.1.83
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.en.md +1 -1
- package/README.md +1 -1
- package/lib/index.js +109 -28
- package/lib/types/adapter.d.ts +19 -0
- package/lib/types/request-log/store.d.ts +14 -4
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -152,7 +152,7 @@ The API key comes from the Harness credential store (reference name `OPENCODE_GO
|
|
|
152
152
|
- **Content split**: below the body size the detail shows **Text X / Images Y** — images estimated as base64 per deduped attachment (each capped at the route's encoded target), text as the residual, so the two always sum to the total. Image-dominated turns read at a glance.
|
|
153
153
|
- Session ids are **always stored whole**: an id is a conversation identity, not a credential, and a truncated one reads two conversations as one.
|
|
154
154
|
- The log is a ring buffer (oldest dropped first) that dies with the process; snapshots are read on open and on manual refresh, plus once a second while any request is running, so a page nobody opens costs no traffic.
|
|
155
|
-
- The credential never enters a record: the recorder takes no headers argument at all, only the allowlisted session id and user agent. A request gets a "Requesting" placeholder row the moment it starts (the status column counts its seconds) and the row is completed in place when it ends. Failed requests are recorded with their error code
|
|
155
|
+
- The credential never enters a record: the recorder takes no headers argument at all, only the allowlisted session id and user agent. A request gets a "Requesting" placeholder row the moment it starts (the status column counts its seconds) and the row is completed in place when it ends. Failed requests are recorded with their error code — including failures before the first byte (unresolvable models, missing credentials), whose rows carry the reason but no usage. A stream the consumer abandoned early is recorded as `ABORTED` instead of vanishing silently.
|
|
156
156
|
|
|
157
157
|
**Images**: an over-budget image is refused explicitly rather than silently downscaled or dropped. The budgets ride one preset `imageBudgetPreset` (three choices, default `dsh`), not three numbers:
|
|
158
158
|
|
package/README.md
CHANGED
|
@@ -151,7 +151,7 @@ API Key 通过 Harness 凭证库提供(引用名 `OPENCODE_GO_API_KEY`),
|
|
|
151
151
|
- **内容拆分**:明细里"请求体"下面跟一行"文本 X /图片 Y"——图片是各去重后附件按 base64 估的占比(单张按路由的编码上限封顶),文本是总数减图片的剩余,两者恒等于请求体总数。图片 dominant 的请求一眼就能看出来。
|
|
152
152
|
- 会话 id **始终完整记录**(它是会话身份,不是凭据;截断后两个会话会读成一个)。
|
|
153
153
|
- 记录的是环形缓冲(超限丢最旧),进程重启即清;页面平时只在打开与手动刷新时读快照,有请求中时每秒自动刷新一次直到结束,没人打开的页面零流量。
|
|
154
|
-
- 凭据永远不进记录:记录器根本不接收请求头参数,只收白名单里的会话 id 与 UA;请求一开始就有一条"请求中"
|
|
154
|
+
- 凭据永远不进记录:记录器根本不接收请求头参数,只收白名单里的会话 id 与 UA;请求一开始就有一条"请求中"占位行(状态列读秒),结束时原位补全为完整记录。失败的请求同样记一条(含错误码)——包括目录解析失败(未知模型)与凭证缺失这类"第一个字节之前"的失败,行里没有用量只有原因;消费者提前终止的流记一条 `ABORTED`,不再无声丢失。
|
|
155
155
|
|
|
156
156
|
**图片**:三个字段各管一层,超限的图片会被明确拒绝,而不是静默降质或丢弃:
|
|
157
157
|
|
package/lib/index.js
CHANGED
|
@@ -76907,30 +76907,113 @@ var OpencodeGoAdapter = class extends LlmAdapter {
|
|
|
76907
76907
|
"UNSUPPORTED_REASONING_EFFORT"
|
|
76908
76908
|
);
|
|
76909
76909
|
}
|
|
76910
|
+
/**
|
|
76911
|
+
* Open the request's log row.
|
|
76912
|
+
*
|
|
76913
|
+
* Called before any wait the request may hit — the catalog included — so
|
|
76914
|
+
* a cold catalog reads as preparation instead of invisible time, and a
|
|
76915
|
+
* failure before the first byte still leaves a row behind. Everything
|
|
76916
|
+
* here is already known or pure: the protocol arrives later, through
|
|
76917
|
+
* `identify`, once the catalog has answered.
|
|
76918
|
+
*/
|
|
76919
|
+
startSpan(options, config, protocol) {
|
|
76920
|
+
const containsImage = options.messages.some((message) => contentHasImage2(message.content));
|
|
76921
|
+
const headers = providerHeaders(options.sessionId === void 0 ? void 0 : String(options.sessionId));
|
|
76922
|
+
const imageBudget = IMAGE_BUDGETS[config.imageBudgetPreset];
|
|
76923
|
+
const imageBytes = containsImage ? estimateImageWireBytes(options.messages, imageBudget.maxBytes) : void 0;
|
|
76924
|
+
return this.options.requestLog?.start({
|
|
76925
|
+
model: options.model,
|
|
76926
|
+
sessionId: options.sessionId === void 0 ? void 0 : String(options.sessionId),
|
|
76927
|
+
...protocol === void 0 ? {} : { protocol },
|
|
76928
|
+
method: "POST",
|
|
76929
|
+
url: config.baseURL,
|
|
76930
|
+
messageCount: options.messages.length,
|
|
76931
|
+
hasImage: containsImage,
|
|
76932
|
+
toolCount: options.tools?.length ?? 0,
|
|
76933
|
+
...imageBytes === void 0 ? {} : { imageBytes },
|
|
76934
|
+
headers: {
|
|
76935
|
+
...options.sessionId === void 0 ? {} : { session: String(options.sessionId) },
|
|
76936
|
+
...typeof headers["user-agent"] === "string" ? { userAgent: headers["user-agent"] } : {}
|
|
76937
|
+
}
|
|
76938
|
+
});
|
|
76939
|
+
}
|
|
76940
|
+
/**
|
|
76941
|
+
* Record a failure that happened before any request was built.
|
|
76942
|
+
*
|
|
76943
|
+
* Preparation (unlike dispatch) knows no session and no messages, so the
|
|
76944
|
+
* row reads as unbuilt rather than empty — but it still names the model
|
|
76945
|
+
* and the code, instead of leaving the caller with an error the log never
|
|
76946
|
+
* heard of.
|
|
76947
|
+
*/
|
|
76948
|
+
logUnstartedFailure(config, model, error) {
|
|
76949
|
+
this.options.requestLog?.start({
|
|
76950
|
+
model,
|
|
76951
|
+
sessionId: void 0,
|
|
76952
|
+
method: "POST",
|
|
76953
|
+
url: config.baseURL,
|
|
76954
|
+
messageCount: 0,
|
|
76955
|
+
hasImage: false,
|
|
76956
|
+
toolCount: 0,
|
|
76957
|
+
headers: {}
|
|
76958
|
+
})?.finish(false, error instanceof LlmError9 ? error.code : void 0);
|
|
76959
|
+
}
|
|
76910
76960
|
async prepareCall(_provider, model, _signal) {
|
|
76911
76961
|
const config = this.options.config();
|
|
76912
76962
|
assertStreamIdleTimeoutMs(config.streamIdleTimeoutMs);
|
|
76913
|
-
|
|
76963
|
+
let snapshot2;
|
|
76964
|
+
try {
|
|
76965
|
+
snapshot2 = await this.catalogOf(config).forModel(model);
|
|
76966
|
+
} catch (error) {
|
|
76967
|
+
this.logUnstartedFailure(config, model, error);
|
|
76968
|
+
throw error;
|
|
76969
|
+
}
|
|
76914
76970
|
const facts = snapshot2.facts.get(model);
|
|
76915
|
-
if (facts === void 0)
|
|
76971
|
+
if (facts === void 0) {
|
|
76972
|
+
const error = new LlmError9(`opencode-go has no model "${model}"`, "UNKNOWN_MODEL");
|
|
76973
|
+
this.logUnstartedFailure(config, model, error);
|
|
76974
|
+
throw error;
|
|
76975
|
+
}
|
|
76916
76976
|
const prepared = this.modelInfo(withModelLimit(toPiModel(facts, config.baseURL), config.modelLimits));
|
|
76917
76977
|
return {
|
|
76918
76978
|
model: prepared,
|
|
76919
|
-
stream: (options) =>
|
|
76979
|
+
stream: (options) => {
|
|
76980
|
+
const span = this.startSpan(options, config, facts.api);
|
|
76981
|
+
return this.streamWithSnapshot(options, config, snapshot2, span);
|
|
76982
|
+
}
|
|
76920
76983
|
};
|
|
76921
76984
|
}
|
|
76922
76985
|
async *stream(options) {
|
|
76923
76986
|
const config = this.options.config();
|
|
76924
76987
|
assertStreamIdleTimeoutMs(config.streamIdleTimeoutMs);
|
|
76925
|
-
const
|
|
76926
|
-
|
|
76988
|
+
const span = this.startSpan(options, config);
|
|
76989
|
+
let snapshot2;
|
|
76990
|
+
try {
|
|
76991
|
+
snapshot2 = await this.catalogOf(config).forModel(options.model);
|
|
76992
|
+
} catch (error) {
|
|
76993
|
+
span?.finish(false, error instanceof LlmError9 ? error.code : void 0);
|
|
76994
|
+
throw error;
|
|
76995
|
+
}
|
|
76996
|
+
yield* this.streamWithSnapshot(options, config, snapshot2, span);
|
|
76927
76997
|
}
|
|
76928
|
-
async *streamWithSnapshot(options, config, snapshot2) {
|
|
76998
|
+
async *streamWithSnapshot(options, config, snapshot2, outerSpan) {
|
|
76929
76999
|
var _stack = [];
|
|
76930
77000
|
try {
|
|
76931
|
-
|
|
77001
|
+
const span = outerSpan ?? this.startSpan(options, config);
|
|
77002
|
+
const failSpan = (error) => {
|
|
77003
|
+
span?.finish(false, error instanceof LlmError9 ? error.code : void 0);
|
|
77004
|
+
throw error;
|
|
77005
|
+
};
|
|
77006
|
+
try {
|
|
77007
|
+
assertStreamIdleTimeoutMs(config.streamIdleTimeoutMs);
|
|
77008
|
+
} catch (error) {
|
|
77009
|
+
failSpan(error);
|
|
77010
|
+
}
|
|
76932
77011
|
const facts = snapshot2.facts.get(options.model);
|
|
76933
|
-
if (facts === void 0)
|
|
77012
|
+
if (facts === void 0) {
|
|
77013
|
+
const error = new LlmError9(`opencode-go has no model "${options.model}"`, "UNKNOWN_MODEL");
|
|
77014
|
+
span?.finish(false, error.code);
|
|
77015
|
+
throw error;
|
|
77016
|
+
}
|
|
76934
77017
|
const model = withModelLimit(toPiModel(facts, config.baseURL), config.modelLimits);
|
|
76935
77018
|
const rates2 = usableRates(facts.cost);
|
|
76936
77019
|
const stop = stopSequencesFor(model, options.stop);
|
|
@@ -76938,32 +77021,24 @@ var OpencodeGoAdapter = class extends LlmAdapter {
|
|
|
76938
77021
|
const maxTokens = options.maxTokens === void 0 || ceiling === null || ceiling === void 0 ? options.maxTokens : Math.min(options.maxTokens, ceiling);
|
|
76939
77022
|
const apiKey = await this.options.resolveApiKey();
|
|
76940
77023
|
if (apiKey === void 0 || apiKey.length === 0) {
|
|
76941
|
-
|
|
77024
|
+
const error = new LlmError9("dshopencodego: no credential resolved for the opencode-go route", "MISSING_CREDENTIAL");
|
|
77025
|
+
span?.finish(false, error.code);
|
|
77026
|
+
throw error;
|
|
77027
|
+
}
|
|
77028
|
+
let reasoning;
|
|
77029
|
+
try {
|
|
77030
|
+
reasoning = this.resolveReasoningLevel(model, options.reasoningEffort);
|
|
77031
|
+
} catch (error) {
|
|
77032
|
+
failSpan(error);
|
|
76942
77033
|
}
|
|
76943
|
-
const reasoning = this.resolveReasoningLevel(model, options.reasoningEffort);
|
|
76944
77034
|
const consumer = new AbortController();
|
|
76945
77035
|
const upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);
|
|
76946
77036
|
const watchdog = __using(_stack, idleWatchdog(upstream, config.streamIdleTimeoutMs, "LLM_STREAM_IDLE_TIMEOUT"));
|
|
76947
77037
|
let timedOut = false;
|
|
77038
|
+
span?.identify(facts.api);
|
|
76948
77039
|
const containsImage = options.messages.some((message) => contentHasImage2(message.content));
|
|
76949
77040
|
const headers = providerHeaders(options.sessionId === void 0 ? void 0 : String(options.sessionId));
|
|
76950
77041
|
const imageBudget = IMAGE_BUDGETS[config.imageBudgetPreset];
|
|
76951
|
-
const imageBytes = containsImage ? estimateImageWireBytes(options.messages, imageBudget.maxBytes) : void 0;
|
|
76952
|
-
const span = this.options.requestLog?.start({
|
|
76953
|
-
model: options.model,
|
|
76954
|
-
sessionId: options.sessionId === void 0 ? void 0 : String(options.sessionId),
|
|
76955
|
-
protocol: facts.api,
|
|
76956
|
-
method: "POST",
|
|
76957
|
-
url: config.baseURL,
|
|
76958
|
-
messageCount: options.messages.length,
|
|
76959
|
-
hasImage: containsImage,
|
|
76960
|
-
toolCount: options.tools?.length ?? 0,
|
|
76961
|
-
...imageBytes === void 0 ? {} : { imageBytes },
|
|
76962
|
-
headers: {
|
|
76963
|
-
...options.sessionId === void 0 ? {} : { session: String(options.sessionId) },
|
|
76964
|
-
...typeof headers["user-agent"] === "string" ? { userAgent: headers["user-agent"] } : {}
|
|
76965
|
-
}
|
|
76966
|
-
});
|
|
76967
77042
|
const fetchFactory = this.options.fetch;
|
|
76968
77043
|
const requestFetch = span === void 0 || fetchFactory === void 0 ? fetchFactory : () => {
|
|
76969
77044
|
const base = fetchFactory();
|
|
@@ -77937,6 +78012,9 @@ var RequestLog = class {
|
|
|
77937
78012
|
let reported = false;
|
|
77938
78013
|
let finished = false;
|
|
77939
78014
|
return {
|
|
78015
|
+
identify: (protocol) => {
|
|
78016
|
+
if (record2.protocol === void 0) record2.protocol = protocol;
|
|
78017
|
+
},
|
|
77940
78018
|
token: () => {
|
|
77941
78019
|
const now = Date.now();
|
|
77942
78020
|
if (firstTokenAt === void 0) {
|
|
@@ -77969,7 +78047,10 @@ var RequestLog = class {
|
|
|
77969
78047
|
seq,
|
|
77970
78048
|
atMs: startedAt,
|
|
77971
78049
|
model: detail.model,
|
|
77972
|
-
|
|
78050
|
+
// The protocol may have arrived after the row opened, through
|
|
78051
|
+
// `identify` — the running row and the completion read the same
|
|
78052
|
+
// field, so they never disagree.
|
|
78053
|
+
...record2.protocol === void 0 ? {} : { protocol: record2.protocol },
|
|
77973
78054
|
session,
|
|
77974
78055
|
method: record2.method,
|
|
77975
78056
|
url: record2.url,
|
|
@@ -78134,7 +78215,7 @@ function createUpdateChecker(deps) {
|
|
|
78134
78215
|
}
|
|
78135
78216
|
|
|
78136
78217
|
// src/update/version.ts
|
|
78137
|
-
var PLUGIN_VERSION = true ? "0.1.
|
|
78218
|
+
var PLUGIN_VERSION = true ? "0.1.83" : "0.0.0-dev";
|
|
78138
78219
|
|
|
78139
78220
|
// src/update/service.ts
|
|
78140
78221
|
import { TypertRemoteService as TypertRemoteService4 } from "@deepseek-ai/dsh-typert-protocol";
|
package/lib/types/adapter.d.ts
CHANGED
|
@@ -156,6 +156,25 @@ export declare class OpencodeGoAdapter extends LlmAdapter {
|
|
|
156
156
|
providerRetryPolicy(_provider: string): ResolvedRetryPolicy | undefined;
|
|
157
157
|
/** Validate an explicit effort against the model's own levels, without clamping. */
|
|
158
158
|
private resolveReasoningLevel;
|
|
159
|
+
/**
|
|
160
|
+
* Open the request's log row.
|
|
161
|
+
*
|
|
162
|
+
* Called before any wait the request may hit — the catalog included — so
|
|
163
|
+
* a cold catalog reads as preparation instead of invisible time, and a
|
|
164
|
+
* failure before the first byte still leaves a row behind. Everything
|
|
165
|
+
* here is already known or pure: the protocol arrives later, through
|
|
166
|
+
* `identify`, once the catalog has answered.
|
|
167
|
+
*/
|
|
168
|
+
private startSpan;
|
|
169
|
+
/**
|
|
170
|
+
* Record a failure that happened before any request was built.
|
|
171
|
+
*
|
|
172
|
+
* Preparation (unlike dispatch) knows no session and no messages, so the
|
|
173
|
+
* row reads as unbuilt rather than empty — but it still names the model
|
|
174
|
+
* and the code, instead of leaving the caller with an error the log never
|
|
175
|
+
* heard of.
|
|
176
|
+
*/
|
|
177
|
+
private logUnstartedFailure;
|
|
159
178
|
prepareCall(_provider: string, model: string, _signal?: AbortSignal): Promise<PreparedAdapterCall>;
|
|
160
179
|
stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
|
|
161
180
|
private streamWithSnapshot;
|
|
@@ -83,10 +83,12 @@ export interface RequestLogEntry {
|
|
|
83
83
|
*/
|
|
84
84
|
readonly streamMs?: number;
|
|
85
85
|
/**
|
|
86
|
-
* Wall time from request start to the moment the fetch
|
|
87
|
-
* in milliseconds — the local
|
|
88
|
-
*
|
|
89
|
-
*
|
|
86
|
+
* Wall time from request start to the moment the fetch went out,
|
|
87
|
+
* in milliseconds — the whole local path, catalog wait included: the row
|
|
88
|
+
* opens before the catalog is consulted, so a cold catalog reads as
|
|
89
|
+
* preparation rather than invisible time. The rest of the first-token
|
|
90
|
+
* wait belongs to the wire and the gateway. Absent when the request never
|
|
91
|
+
* reached the wire (rejected up front).
|
|
90
92
|
*/
|
|
91
93
|
readonly prepMs?: number;
|
|
92
94
|
/** Wall time from request start to terminal event, in milliseconds. */
|
|
@@ -218,6 +220,14 @@ export interface RequestSpan {
|
|
|
218
220
|
request(method: string, url: string, requestBytes?: number): void;
|
|
219
221
|
/** Mark the response status line's arrival. */
|
|
220
222
|
status(status: number): void;
|
|
223
|
+
/**
|
|
224
|
+
* Fill in the wire protocol once the catalog has answered.
|
|
225
|
+
*
|
|
226
|
+
* The row opens before the catalog wait (so the wait itself is recorded),
|
|
227
|
+
* but the protocol is only known after it. Called at most once, before
|
|
228
|
+
* `finish`; a second call keeps the first.
|
|
229
|
+
*/
|
|
230
|
+
identify(protocol: WireProtocol): void;
|
|
221
231
|
/**
|
|
222
232
|
* Close the span and complete its running row in place.
|
|
223
233
|
* @param ok - whether the request completed.
|
package/package.json
CHANGED