@dan-ai-studio/dshopencodego 0.1.43 → 0.1.44
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 +48 -28
- package/README.md +47 -27
- package/lib/client.js +47 -1
- package/lib/index.js +321 -123
- package/lib/types/adapter.d.ts +26 -3
- package/lib/types/catalog/go-doc.d.ts +9 -5
- package/lib/types/catalog/index.d.ts +22 -7
- package/lib/types/catalog/service.d.ts +44 -1
- package/lib/types/config.d.ts +4 -3
- package/lib/types/conversion/replay.d.ts +30 -1
- package/lib/types/models.d.ts +14 -0
- package/lib/types/usage/meter.d.ts +89 -2
- package/package.json +20 -21
- package/lib/types/adapter.js +0 -384
- package/lib/types/catalog/constants.js +0 -18
- package/lib/types/catalog/contract.js +0 -147
- package/lib/types/catalog/gateway.js +0 -59
- package/lib/types/catalog/go-doc.js +0 -192
- package/lib/types/catalog/index.js +0 -377
- package/lib/types/catalog/json-response.js +0 -73
- package/lib/types/catalog/metadata.js +0 -278
- package/lib/types/catalog/protocol.js +0 -87
- package/lib/types/catalog/reading.js +0 -73
- package/lib/types/catalog/service.js +0 -45
- package/lib/types/config.js +0 -91
- package/lib/types/conversion/context.js +0 -212
- package/lib/types/conversion/index.js +0 -7
- package/lib/types/conversion/replay.js +0 -242
- package/lib/types/conversion/stream.js +0 -221
- package/lib/types/go-limits.js +0 -91
- package/lib/types/index.js +0 -198
- package/lib/types/models.js +0 -86
- package/lib/types/remotes.js +0 -29
- package/lib/types/session-header.js +0 -49
- package/lib/types/update/check.js +0 -138
- package/lib/types/update/contract.js +0 -64
- package/lib/types/update/index.js +0 -10
- package/lib/types/update/service.js +0 -39
- package/lib/types/update/version.js +0 -16
- package/lib/types/usage/contract.js +0 -106
- package/lib/types/usage/index.js +0 -8
- package/lib/types/usage/meter.js +0 -65
- package/lib/types/usage/service.js +0 -74
- package/lib/types/usage/windows.js +0 -84
package/lib/index.js
CHANGED
|
@@ -42702,31 +42702,32 @@ function parseGoDocument(markdown) {
|
|
|
42702
42702
|
}
|
|
42703
42703
|
return { quotas, protocols };
|
|
42704
42704
|
}
|
|
42705
|
-
async function
|
|
42706
|
-
const
|
|
42707
|
-
|
|
42708
|
-
|
|
42709
|
-
|
|
42710
|
-
|
|
42711
|
-
|
|
42712
|
-
|
|
42713
|
-
|
|
42705
|
+
async function readSource(url, signal) {
|
|
42706
|
+
const timeout = AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS);
|
|
42707
|
+
try {
|
|
42708
|
+
const response = await fetch(url, {
|
|
42709
|
+
redirect: "error",
|
|
42710
|
+
headers: { ...attributionHeaders2(), accept: "text/plain", "cache-control": "no-cache" },
|
|
42711
|
+
signal: signal === void 0 ? timeout : AbortSignal.any([signal, timeout])
|
|
42712
|
+
});
|
|
42713
|
+
if (!response.ok) {
|
|
42714
|
+
await response.body?.cancel().catch(() => {
|
|
42714
42715
|
});
|
|
42715
|
-
|
|
42716
|
-
await response.body?.cancel().catch(() => {
|
|
42717
|
-
});
|
|
42718
|
-
failures.push(`${url} answered HTTP ${response.status}`);
|
|
42719
|
-
continue;
|
|
42720
|
-
}
|
|
42721
|
-
const document = parseGoDocument(await readBoundedText(response, GO_DOC_MAX_BYTES));
|
|
42722
|
-
if (document.quotas.size === 0) {
|
|
42723
|
-
failures.push(`${url} carried no allowance table`);
|
|
42724
|
-
continue;
|
|
42725
|
-
}
|
|
42726
|
-
return document;
|
|
42727
|
-
} catch (error) {
|
|
42728
|
-
failures.push(`${url}: ${error instanceof Error ? error.message : String(error)}`);
|
|
42716
|
+
return { failure: `${url} answered HTTP ${response.status}` };
|
|
42729
42717
|
}
|
|
42718
|
+
const document = parseGoDocument(await readBoundedText(response, GO_DOC_MAX_BYTES));
|
|
42719
|
+
if (document.quotas.size === 0) return { failure: `${url} carried no allowance table` };
|
|
42720
|
+
return { document };
|
|
42721
|
+
} catch (error) {
|
|
42722
|
+
return { failure: `${url}: ${error instanceof Error ? error.message : String(error)}` };
|
|
42723
|
+
}
|
|
42724
|
+
}
|
|
42725
|
+
async function fetchGoDocument(signal) {
|
|
42726
|
+
const attempts = await Promise.all(GO_DOC_URLS.map((url) => readSource(url, signal)));
|
|
42727
|
+
const failures = [];
|
|
42728
|
+
for (const attempt of attempts) {
|
|
42729
|
+
if ("document" in attempt) return attempt.document;
|
|
42730
|
+
failures.push(attempt.failure);
|
|
42730
42731
|
}
|
|
42731
42732
|
throw new LlmError3(
|
|
42732
42733
|
`could not read the Go documentation (${failures.join("; ")})`,
|
|
@@ -42996,12 +42997,19 @@ var OpencodeGoCatalog = class {
|
|
|
42996
42997
|
constructor(options) {
|
|
42997
42998
|
this.options = options;
|
|
42998
42999
|
}
|
|
42999
|
-
/**
|
|
43000
|
-
|
|
43000
|
+
/**
|
|
43001
|
+
* The cached snapshot, refreshed when it is older than the configured TTL.
|
|
43002
|
+
* @param force - revalidate every source, ignoring the cache.
|
|
43003
|
+
* @param signal - caller cancellation, forwarded to the documentation read,
|
|
43004
|
+
* the slowest source and the only one a caller can cancel; a build already
|
|
43005
|
+
* in flight keeps the signal of the caller that started it, because one
|
|
43006
|
+
* shared snapshot cannot carry two different cancellations.
|
|
43007
|
+
*/
|
|
43008
|
+
snapshot(force = false, signal) {
|
|
43001
43009
|
if (!force && this.served !== void 0 && Date.now() - this.served.fetchedAtMs < this.options.refreshMs) {
|
|
43002
43010
|
return Promise.resolve(this.served);
|
|
43003
43011
|
}
|
|
43004
|
-
this.pending ??= this.build().then((snapshot2) => {
|
|
43012
|
+
this.pending ??= this.build(signal).then((snapshot2) => {
|
|
43005
43013
|
this.served = snapshot2;
|
|
43006
43014
|
return snapshot2;
|
|
43007
43015
|
}).finally(() => {
|
|
@@ -43015,14 +43023,15 @@ var OpencodeGoCatalog = class {
|
|
|
43015
43023
|
* A model this build has never seen forces one revalidation even inside the
|
|
43016
43024
|
* TTL, because "unknown" and "newly added" look identical from here.
|
|
43017
43025
|
* @param id - the requested model id.
|
|
43026
|
+
* @param signal - caller cancellation, forwarded to a forced revalidation.
|
|
43018
43027
|
* @returns the snapshot holding the model.
|
|
43019
43028
|
* @throws {LlmError} `MODEL_METADATA_UNAVAILABLE` when the gateway advertises
|
|
43020
43029
|
* the id but this build cannot configure it, naming the reason.
|
|
43021
43030
|
*/
|
|
43022
|
-
async forModel(id) {
|
|
43031
|
+
async forModel(id, signal) {
|
|
43023
43032
|
let snapshot2 = await this.snapshot();
|
|
43024
43033
|
if (!snapshot2.facts.has(id) && !snapshot2.unavailable.has(id)) {
|
|
43025
|
-
snapshot2 = await this.snapshot(true);
|
|
43034
|
+
snapshot2 = await this.snapshot(true, signal);
|
|
43026
43035
|
}
|
|
43027
43036
|
const reason = snapshot2.unavailable.get(id);
|
|
43028
43037
|
if (reason !== void 0) {
|
|
@@ -43033,14 +43042,17 @@ var OpencodeGoCatalog = class {
|
|
|
43033
43042
|
}
|
|
43034
43043
|
return snapshot2;
|
|
43035
43044
|
}
|
|
43036
|
-
/**
|
|
43037
|
-
|
|
43045
|
+
/**
|
|
43046
|
+
* Revalidate every source, reusing the metadata document when it is unchanged.
|
|
43047
|
+
* @param signal - caller cancellation, forwarded to the documentation read.
|
|
43048
|
+
*/
|
|
43049
|
+
async build(signal) {
|
|
43038
43050
|
const builtin = builtinModels(this.options.baseURL);
|
|
43039
43051
|
const readDocument = this.options.readDocument ?? fetchGoDocument;
|
|
43040
43052
|
const [listing, metadata, documentation] = await Promise.allSettled([
|
|
43041
43053
|
fetchModelIds(this.options.baseURL),
|
|
43042
43054
|
this.refreshMetadata(builtin),
|
|
43043
|
-
readDocument()
|
|
43055
|
+
readDocument(signal)
|
|
43044
43056
|
]);
|
|
43045
43057
|
if (metadata.status === "rejected") {
|
|
43046
43058
|
this.options.observers?.onFallback?.({
|
|
@@ -43206,8 +43218,8 @@ function inferFamily(id) {
|
|
|
43206
43218
|
const lower = id.toLowerCase();
|
|
43207
43219
|
return ["grok", "gpt", "muse"].some((family) => lower.startsWith(family)) ? "openai-responses" : "openai-completions";
|
|
43208
43220
|
}
|
|
43209
|
-
async function discoverCatalogModels(catalog) {
|
|
43210
|
-
const snapshot2 = await catalog.snapshot(true);
|
|
43221
|
+
async function discoverCatalogModels(catalog, signal) {
|
|
43222
|
+
const snapshot2 = await catalog.snapshot(true, signal);
|
|
43211
43223
|
if (!snapshot2.live) {
|
|
43212
43224
|
const detail = snapshot2.listingFailure instanceof LlmError4 ? snapshot2.listingFailure.message : "the live model listing is unreachable";
|
|
43213
43225
|
throw new LlmError4(`dshopencodego: ${detail}; refresh the model list to retry`, "DISCOVERY_FAILED", {
|
|
@@ -44129,6 +44141,7 @@ function readConfig(config) {
|
|
|
44129
44141
|
return field?.get === void 0 ? [] : [[key, field.get()]];
|
|
44130
44142
|
}));
|
|
44131
44143
|
}
|
|
44144
|
+
var PLAIN_HTTP_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]", "::1"]);
|
|
44132
44145
|
function assertBaseURL(raw) {
|
|
44133
44146
|
let url;
|
|
44134
44147
|
try {
|
|
@@ -44139,6 +44152,11 @@ function assertBaseURL(raw) {
|
|
|
44139
44152
|
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
44140
44153
|
throw new Error(`dshopencodego: baseURL "${raw}" must be http or https`);
|
|
44141
44154
|
}
|
|
44155
|
+
if (url.protocol === "http:" && !PLAIN_HTTP_HOSTS.has(url.hostname)) {
|
|
44156
|
+
throw new Error(
|
|
44157
|
+
`dshopencodego: baseURL "${raw}" must use https outside localhost \u2014 a plain-http endpoint would send the API key in clear text`
|
|
44158
|
+
);
|
|
44159
|
+
}
|
|
44142
44160
|
if (url.search.length > 0 || url.hash.length > 0) {
|
|
44143
44161
|
throw new Error(`dshopencodego: baseURL "${raw}" must not carry a query or fragment`);
|
|
44144
44162
|
}
|
|
@@ -44153,6 +44171,10 @@ import { requestImageDimensions } from "@deepseek-ai/dsh-attachment";
|
|
|
44153
44171
|
|
|
44154
44172
|
// src/conversion/replay.ts
|
|
44155
44173
|
import { LlmError as LlmError5 } from "@deepseek-ai/dsh-llm";
|
|
44174
|
+
var REPLAYABLE_STOP_REASONS = ["stop", "length", "toolUse", "error", "aborted"];
|
|
44175
|
+
function replayableStopReason(stopReason) {
|
|
44176
|
+
return REPLAYABLE_STOP_REASONS.includes(stopReason) ? stopReason : "error";
|
|
44177
|
+
}
|
|
44156
44178
|
function parseArguments(raw) {
|
|
44157
44179
|
try {
|
|
44158
44180
|
const parsed = JSON.parse(raw);
|
|
@@ -44184,7 +44206,7 @@ function toPiReplayState(message, requestedModel = message.model) {
|
|
|
44184
44206
|
...responseModel === void 0 ? {} : { responseModel },
|
|
44185
44207
|
...message.responseId === void 0 ? {} : { responseId: message.responseId },
|
|
44186
44208
|
...message.providerThinkingLevel === void 0 ? {} : { providerThinkingLevel: message.providerThinkingLevel },
|
|
44187
|
-
stopReason: message.stopReason
|
|
44209
|
+
stopReason: replayableStopReason(message.stopReason)
|
|
44188
44210
|
};
|
|
44189
44211
|
return {
|
|
44190
44212
|
response,
|
|
@@ -44226,7 +44248,7 @@ function readReplayState(value) {
|
|
|
44226
44248
|
for (const key of ["api", "provider", "model"]) {
|
|
44227
44249
|
if (typeof response[key] !== "string" || response[key].length === 0) return invalidReplay(`${key} must be a non-empty string`);
|
|
44228
44250
|
}
|
|
44229
|
-
if (!
|
|
44251
|
+
if (!REPLAYABLE_STOP_REASONS.includes(String(response["stopReason"]))) {
|
|
44230
44252
|
return invalidReplay("unknown stopReason");
|
|
44231
44253
|
}
|
|
44232
44254
|
for (const key of ["responseModel", "responseId", "providerThinkingLevel"]) {
|
|
@@ -44804,6 +44826,10 @@ function isModelEnabled(model, visibility) {
|
|
|
44804
44826
|
if (typeof explicit === "boolean") return explicit;
|
|
44805
44827
|
return typeof model.recommended === "boolean" ? model.recommended : model.deprecated !== true;
|
|
44806
44828
|
}
|
|
44829
|
+
function unadvertisedVisibilityIds(visibility, advertised) {
|
|
44830
|
+
const known = new Set(advertised);
|
|
44831
|
+
return Object.keys(visibility).filter((id) => !known.has(id)).sort();
|
|
44832
|
+
}
|
|
44807
44833
|
function validReleaseDate2(value) {
|
|
44808
44834
|
return typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value) && Number.isFinite(Date.parse(value)) && new Date(value).toISOString().slice(0, 10) === value;
|
|
44809
44835
|
}
|
|
@@ -44834,6 +44860,85 @@ function providerHeaders(sessionId) {
|
|
|
44834
44860
|
};
|
|
44835
44861
|
}
|
|
44836
44862
|
|
|
44863
|
+
// src/usage/meter.ts
|
|
44864
|
+
function usableRates(cost) {
|
|
44865
|
+
if (cost === void 0) return void 0;
|
|
44866
|
+
if (!(cost.input > 0) && !(cost.output > 0)) return void 0;
|
|
44867
|
+
return {
|
|
44868
|
+
input: cost.input,
|
|
44869
|
+
output: cost.output,
|
|
44870
|
+
...cost.cacheRead > 0 ? { cacheRead: cost.cacheRead } : {},
|
|
44871
|
+
...cost.cacheWrite > 0 ? { cacheWrite: cost.cacheWrite } : {}
|
|
44872
|
+
};
|
|
44873
|
+
}
|
|
44874
|
+
var EMPTY3 = {
|
|
44875
|
+
calls: 0,
|
|
44876
|
+
inputTokens: 0,
|
|
44877
|
+
outputTokens: 0,
|
|
44878
|
+
cacheReadTokens: 0,
|
|
44879
|
+
cacheWriteTokens: 0,
|
|
44880
|
+
totalTokens: 0
|
|
44881
|
+
};
|
|
44882
|
+
function spendOf(usage, rates2) {
|
|
44883
|
+
const fallback = rates2.input > 0 ? rates2.input : rates2.output;
|
|
44884
|
+
const cacheRead = usage.cacheReadTokens ?? 0;
|
|
44885
|
+
const cacheWrite = usage.cacheWriteTokens ?? 0;
|
|
44886
|
+
return (usage.inputTokens * rates2.input + usage.outputTokens * rates2.output + cacheRead * (rates2.cacheRead ?? fallback) + cacheWrite * (rates2.cacheWrite ?? fallback)) / 1e6;
|
|
44887
|
+
}
|
|
44888
|
+
function add(totals, usage) {
|
|
44889
|
+
const inputTokens = totals.inputTokens + usage.inputTokens;
|
|
44890
|
+
const outputTokens = totals.outputTokens + usage.outputTokens;
|
|
44891
|
+
const cacheReadTokens = totals.cacheReadTokens + (usage.cacheReadTokens ?? 0);
|
|
44892
|
+
const cacheWriteTokens = totals.cacheWriteTokens + (usage.cacheWriteTokens ?? 0);
|
|
44893
|
+
const totalTokens = inputTokens + outputTokens + cacheReadTokens + cacheWriteTokens;
|
|
44894
|
+
const reasoningTokens = (totals.reasoningTokens ?? 0) + (usage.reasoningTokens ?? 0);
|
|
44895
|
+
const rates2 = usage.rates;
|
|
44896
|
+
const billedTokens = usage.inputTokens + usage.outputTokens + (usage.cacheReadTokens ?? 0) + (usage.cacheWriteTokens ?? 0);
|
|
44897
|
+
const unpricedTokens = (totals.unpricedTokens ?? 0) + (rates2 === void 0 ? billedTokens : 0);
|
|
44898
|
+
const estimatedUSD = rates2 === void 0 ? totals.estimatedUSD : (totals.estimatedUSD ?? 0) + spendOf(usage, rates2);
|
|
44899
|
+
return {
|
|
44900
|
+
calls: totals.calls + 1,
|
|
44901
|
+
inputTokens,
|
|
44902
|
+
outputTokens,
|
|
44903
|
+
cacheReadTokens,
|
|
44904
|
+
cacheWriteTokens,
|
|
44905
|
+
// The four counts are disjoint (uncached input, cached input, output), so
|
|
44906
|
+
// their sum is the billed total. The provider's own `totalTokens` is not
|
|
44907
|
+
// used: it is optional, and mixing the two would double-count a cache read.
|
|
44908
|
+
totalTokens,
|
|
44909
|
+
...reasoningTokens > 0 ? { reasoningTokens } : {},
|
|
44910
|
+
...estimatedUSD === void 0 ? {} : { estimatedUSD },
|
|
44911
|
+
// Only meaningful next to an estimate: a reading with no priced call at all
|
|
44912
|
+
// says so by omitting `estimatedUSD`, and naming every token as "unpriced"
|
|
44913
|
+
// there would just restate the total.
|
|
44914
|
+
...estimatedUSD === void 0 || unpricedTokens === 0 ? {} : { unpricedTokens }
|
|
44915
|
+
};
|
|
44916
|
+
}
|
|
44917
|
+
var UsageMeter = class {
|
|
44918
|
+
sinceMs = Date.now();
|
|
44919
|
+
totals = EMPTY3;
|
|
44920
|
+
byModel = /* @__PURE__ */ new Map();
|
|
44921
|
+
/**
|
|
44922
|
+
* Record one completed call.
|
|
44923
|
+
* @param model - the model id the call was made with.
|
|
44924
|
+
* @param usage - the provider's own usage for that call, with the rates that
|
|
44925
|
+
* were published for the model at the time it ran.
|
|
44926
|
+
*/
|
|
44927
|
+
record(model, usage) {
|
|
44928
|
+
this.totals = add(this.totals, usage);
|
|
44929
|
+
this.byModel.set(model, add(this.byModel.get(model) ?? EMPTY3, usage));
|
|
44930
|
+
}
|
|
44931
|
+
/** The current reading, busiest model first. */
|
|
44932
|
+
snapshot(now = Date.now()) {
|
|
44933
|
+
return {
|
|
44934
|
+
sinceMs: this.sinceMs,
|
|
44935
|
+
atMs: now,
|
|
44936
|
+
totals: this.totals,
|
|
44937
|
+
models: [...this.byModel.entries()].map(([model, totals]) => ({ model, ...totals })).sort((left, right) => right.totalTokens - left.totalTokens || left.model.localeCompare(right.model))
|
|
44938
|
+
};
|
|
44939
|
+
}
|
|
44940
|
+
};
|
|
44941
|
+
|
|
44837
44942
|
// src/adapter.ts
|
|
44838
44943
|
var DISABLES_THINKING_WHEN_UNSET = /* @__PURE__ */ new Set(["deepseek", "zai", "qwen", "qwen-chat-template"]);
|
|
44839
44944
|
function withModelLimit(model, limits) {
|
|
@@ -44853,6 +44958,13 @@ function stopSequencesFor(model, stop) {
|
|
|
44853
44958
|
"UNSUPPORTED_OPTION"
|
|
44854
44959
|
);
|
|
44855
44960
|
}
|
|
44961
|
+
async function* observingTerminalUsage(events, observe) {
|
|
44962
|
+
for await (const event of events) {
|
|
44963
|
+
if (event.type === "done") observe(event.message.usage);
|
|
44964
|
+
else if (event.type === "error") observe(event.error.usage);
|
|
44965
|
+
yield event;
|
|
44966
|
+
}
|
|
44967
|
+
}
|
|
44856
44968
|
var OpencodeGoAdapter = class extends LlmAdapter {
|
|
44857
44969
|
cache;
|
|
44858
44970
|
options;
|
|
@@ -44888,6 +45000,28 @@ var OpencodeGoAdapter = class extends LlmAdapter {
|
|
|
44888
45000
|
providerInfo(provider) {
|
|
44889
45001
|
return { id: provider, name: DISPLAY_NAME };
|
|
44890
45002
|
}
|
|
45003
|
+
/**
|
|
45004
|
+
* This route declares no request-image pricing, and that is a decision
|
|
45005
|
+
* rather than an omission.
|
|
45006
|
+
*
|
|
45007
|
+
* `priceImages` must answer with the visual tokens the wire actually carries
|
|
45008
|
+
* for every image occurrence, and nothing available to this plugin can
|
|
45009
|
+
* answer that. The route fronts several model families behind three
|
|
45010
|
+
* protocols whose vision accounting differs and is not published by the
|
|
45011
|
+
* gateway; the catalog carries text rates only (`ModelCost` is input,
|
|
45012
|
+
* output, cache read and cache write) and states no vision rule; and the one
|
|
45013
|
+
* implementation shipped upstream anchors on a single provider's own
|
|
45014
|
+
* published accounting (`dsh-llm-deepseek`). An invented number would not
|
|
45015
|
+
* fail loudly: the token meter sums these values into its surface estimate
|
|
45016
|
+
* and the retention policy spends real budget against them, so a wrong
|
|
45017
|
+
* answer is worse than the contract's own default — declaring none, which
|
|
45018
|
+
* leaves every consumer on its neutral estimate. Revisit this once the
|
|
45019
|
+
* gateway's per-family visual accounting is known and checked against the
|
|
45020
|
+
* usage it reports.
|
|
45021
|
+
*/
|
|
45022
|
+
imageRequestPricing(_provider, _model) {
|
|
45023
|
+
return void 0;
|
|
45024
|
+
}
|
|
44891
45025
|
async listModels(_provider) {
|
|
44892
45026
|
const config = this.options.config();
|
|
44893
45027
|
const snapshot2 = await this.catalogOf(config).snapshot();
|
|
@@ -44977,6 +45111,7 @@ var OpencodeGoAdapter = class extends LlmAdapter {
|
|
|
44977
45111
|
const facts = snapshot2.facts.get(options.model);
|
|
44978
45112
|
if (facts === void 0) throw new LlmError8(`opencode-go has no model "${options.model}"`, "UNKNOWN_MODEL");
|
|
44979
45113
|
const model = withModelLimit(toPiModel(facts, config.baseURL), config.modelLimits);
|
|
45114
|
+
const rates2 = usableRates(facts.cost);
|
|
44980
45115
|
const stop = stopSequencesFor(model, options.stop);
|
|
44981
45116
|
const ceiling = config.modelLimits[model.id]?.maxTokens;
|
|
44982
45117
|
const maxTokens = options.maxTokens === void 0 || ceiling === null || ceiling === void 0 ? options.maxTokens : Math.min(options.maxTokens, ceiling);
|
|
@@ -44988,6 +45123,7 @@ var OpencodeGoAdapter = class extends LlmAdapter {
|
|
|
44988
45123
|
const consumer = new AbortController();
|
|
44989
45124
|
const upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);
|
|
44990
45125
|
const watchdog = __using(_stack, idleWatchdog(upstream, config.streamIdleTimeoutMs, "LLM_STREAM_IDLE_TIMEOUT"));
|
|
45126
|
+
let timedOut = false;
|
|
44991
45127
|
try {
|
|
44992
45128
|
const containsImage = options.messages.some((message) => contentHasImage2(message.content));
|
|
44993
45129
|
if (containsImage && !model.input.includes("image")) {
|
|
@@ -45008,7 +45144,8 @@ var OpencodeGoAdapter = class extends LlmAdapter {
|
|
|
45008
45144
|
};
|
|
45009
45145
|
}
|
|
45010
45146
|
const context = imageRequest === void 0 ? await toPiContext(options, void 0, this.options.onReplayDegrade) : await toPiContext({ ...options, signal: watchdog.signal }, imageRequest, this.options.onReplayDegrade);
|
|
45011
|
-
|
|
45147
|
+
let reportedReasoning;
|
|
45148
|
+
const events = observingTerminalUsage(snapshot2.provider.streamSimple(model, normalizeContext(context), {
|
|
45012
45149
|
apiKey,
|
|
45013
45150
|
...reasoning === void 0 || reasoning === "off" ? {} : { reasoning },
|
|
45014
45151
|
...options.temperature === void 0 ? {} : { temperature: options.temperature },
|
|
@@ -45025,6 +45162,8 @@ var OpencodeGoAdapter = class extends LlmAdapter {
|
|
|
45025
45162
|
// The agent recovery layer owns visible attempts; one adapter call is
|
|
45026
45163
|
// one SDK attempt.
|
|
45027
45164
|
maxRetries: 0
|
|
45165
|
+
}), (usage) => {
|
|
45166
|
+
reportedReasoning = usage.reasoning;
|
|
45028
45167
|
});
|
|
45029
45168
|
const iterator = toStreamChunks(events, model.contextWindow, options.signal, model.id)[Symbol.asyncIterator]();
|
|
45030
45169
|
let exhausted = false;
|
|
@@ -45039,21 +45178,32 @@ var OpencodeGoAdapter = class extends LlmAdapter {
|
|
|
45039
45178
|
return;
|
|
45040
45179
|
}
|
|
45041
45180
|
if (result.value.type === "usage") {
|
|
45042
|
-
this.options.onUsage?.({
|
|
45181
|
+
this.options.onUsage?.({
|
|
45182
|
+
model: options.model,
|
|
45183
|
+
usage: {
|
|
45184
|
+
...result.value.usage,
|
|
45185
|
+
// pi-ai reports this as part of `output`, and `mapUsage` drops
|
|
45186
|
+
// it; the meter records it without adding it to a total.
|
|
45187
|
+
...reportedReasoning === void 0 || reportedReasoning <= 0 ? {} : { reasoningTokens: reportedReasoning },
|
|
45188
|
+
...rates2 === void 0 ? {} : { rates: rates2 }
|
|
45189
|
+
}
|
|
45190
|
+
});
|
|
45043
45191
|
}
|
|
45044
45192
|
yield result.value;
|
|
45045
45193
|
}
|
|
45046
45194
|
} finally {
|
|
45047
45195
|
if (!exhausted) {
|
|
45196
|
+
timedOut = timeoutOf(watchdog.signal, "LLM_STREAM_IDLE_TIMEOUT") !== void 0;
|
|
45048
45197
|
consumer.abort("opencode-go stream consumer stopped");
|
|
45049
45198
|
try {
|
|
45050
|
-
|
|
45199
|
+
void Promise.resolve(iterator.return(void 0)).catch(() => {
|
|
45200
|
+
});
|
|
45051
45201
|
} catch (_abortedSdkTeardown) {
|
|
45052
45202
|
}
|
|
45053
45203
|
}
|
|
45054
45204
|
}
|
|
45055
45205
|
} catch (error) {
|
|
45056
|
-
if (timeoutOf(watchdog.signal, "LLM_STREAM_IDLE_TIMEOUT") !== void 0) {
|
|
45206
|
+
if (timedOut || timeoutOf(watchdog.signal, "LLM_STREAM_IDLE_TIMEOUT") !== void 0) {
|
|
45057
45207
|
throw new LlmError8("opencode-go stream idle timeout", "TIMEOUT", { cause: error });
|
|
45058
45208
|
}
|
|
45059
45209
|
if (options.signal?.aborted) {
|
|
@@ -45266,13 +45416,20 @@ function parseTotals(value) {
|
|
|
45266
45416
|
if (typeof entry !== "number" || !Number.isFinite(entry) || entry < 0) throw new Error(`invalid meter field "${key}"`);
|
|
45267
45417
|
return entry;
|
|
45268
45418
|
};
|
|
45419
|
+
const optionalCount = (key) => row[key] === void 0 ? void 0 : count(key);
|
|
45420
|
+
const reasoningTokens = optionalCount("reasoningTokens");
|
|
45421
|
+
const estimatedUSD = optionalCount("estimatedUSD");
|
|
45422
|
+
const unpricedTokens = optionalCount("unpricedTokens");
|
|
45269
45423
|
return {
|
|
45270
45424
|
calls: count("calls"),
|
|
45271
45425
|
inputTokens: count("inputTokens"),
|
|
45272
45426
|
outputTokens: count("outputTokens"),
|
|
45273
45427
|
cacheReadTokens: count("cacheReadTokens"),
|
|
45274
45428
|
cacheWriteTokens: count("cacheWriteTokens"),
|
|
45275
|
-
totalTokens: count("totalTokens")
|
|
45429
|
+
totalTokens: count("totalTokens"),
|
|
45430
|
+
...reasoningTokens === void 0 ? {} : { reasoningTokens },
|
|
45431
|
+
...estimatedUSD === void 0 ? {} : { estimatedUSD },
|
|
45432
|
+
...unpricedTokens === void 0 ? {} : { unpricedTokens }
|
|
45276
45433
|
};
|
|
45277
45434
|
}
|
|
45278
45435
|
function parseGoMeter(value) {
|
|
@@ -45397,6 +45554,8 @@ function catalogReading(snapshot2, visibility, listingFailure, now) {
|
|
|
45397
45554
|
// src/catalog/service.ts
|
|
45398
45555
|
var OpencodeGoCatalogService = class extends TypertRemoteService {
|
|
45399
45556
|
options;
|
|
45557
|
+
/** The last reported set of unadvertised switches, as a comparable fact. */
|
|
45558
|
+
reportedUnadvertised;
|
|
45400
45559
|
constructor(ctx, options) {
|
|
45401
45560
|
super(ctx, "opencodeGoCatalog");
|
|
45402
45561
|
this.options = options;
|
|
@@ -45411,15 +45570,49 @@ var OpencodeGoCatalogService = class extends TypertRemoteService {
|
|
|
45411
45570
|
}
|
|
45412
45571
|
async project(force) {
|
|
45413
45572
|
const catalog = this.options.catalog();
|
|
45414
|
-
|
|
45415
|
-
|
|
45416
|
-
|
|
45417
|
-
|
|
45418
|
-
|
|
45419
|
-
|
|
45420
|
-
|
|
45573
|
+
try {
|
|
45574
|
+
const snapshot2 = await catalog.snapshot(force);
|
|
45575
|
+
const failure = snapshot2.listingFailure;
|
|
45576
|
+
const visibility = this.options.visibility();
|
|
45577
|
+
const reading = catalogReading(
|
|
45578
|
+
snapshot2,
|
|
45579
|
+
visibility,
|
|
45580
|
+
failure === void 0 ? void 0 : failure instanceof LlmError9 || failure instanceof Error ? failure.message : String(failure)
|
|
45581
|
+
);
|
|
45582
|
+
this.reportUnadvertised(snapshot2, visibility);
|
|
45583
|
+
return reading;
|
|
45584
|
+
} catch (error) {
|
|
45585
|
+
throw catalogFailure(error);
|
|
45586
|
+
}
|
|
45587
|
+
}
|
|
45588
|
+
/**
|
|
45589
|
+
* Report switches for ids the gateway never advertised.
|
|
45590
|
+
*
|
|
45591
|
+
* `modelVisibility` is per-id configuration, and an entry naming a model the
|
|
45592
|
+
* gateway does not serve has no effect anywhere — the picker cannot show a
|
|
45593
|
+
* model that is not in the listing, so without this the switch is silent.
|
|
45594
|
+
* (The reverse case, advertised but unbuildable, is `onUnconfigured`.)
|
|
45595
|
+
*
|
|
45596
|
+
* Only a live listing is evidence of absence: a retained snapshot after an
|
|
45597
|
+
* outage would accuse models that merely could not be re-read. The set is
|
|
45598
|
+
* compared as a fact, so re-reading the page does not repeat the warning.
|
|
45599
|
+
* @param snapshot - the snapshot the reading was projected from.
|
|
45600
|
+
* @param visibility - the switches in force for this read.
|
|
45601
|
+
*/
|
|
45602
|
+
reportUnadvertised(snapshot2, visibility) {
|
|
45603
|
+
const missing = snapshot2.live && visibility !== void 0 ? unadvertisedVisibilityIds(visibility, [...snapshot2.facts.keys(), ...snapshot2.unavailable.keys()]) : [];
|
|
45604
|
+
const facts = JSON.stringify(missing);
|
|
45605
|
+
if (facts === this.reportedUnadvertised) return;
|
|
45606
|
+
this.reportedUnadvertised = facts;
|
|
45607
|
+
if (missing.length > 0) this.options.onUnadvertised?.(missing);
|
|
45421
45608
|
}
|
|
45422
45609
|
};
|
|
45610
|
+
function catalogFailure(error) {
|
|
45611
|
+
return new RemoteError("dshopencodego/catalog-unavailable", error instanceof Error ? error.message : "The OpenCode Go catalog is unavailable", {
|
|
45612
|
+
retryable: true,
|
|
45613
|
+
retainPrevious: true
|
|
45614
|
+
}, { cause: error });
|
|
45615
|
+
}
|
|
45423
45616
|
|
|
45424
45617
|
// src/update/check.ts
|
|
45425
45618
|
var PACKAGE_NAME = "@dan-ai-studio/dshopencodego";
|
|
@@ -45435,6 +45628,14 @@ function parseVersion(value) {
|
|
|
45435
45628
|
prerelease: prerelease === void 0 ? void 0 : prerelease.split(".")
|
|
45436
45629
|
};
|
|
45437
45630
|
}
|
|
45631
|
+
function comparePrereleaseIdentifier(left, right) {
|
|
45632
|
+
const leftNumber = /^\d+$/.test(left) ? Number(left) : void 0;
|
|
45633
|
+
const rightNumber = /^\d+$/.test(right) ? Number(right) : void 0;
|
|
45634
|
+
if (leftNumber !== void 0 && rightNumber !== void 0) return leftNumber - rightNumber;
|
|
45635
|
+
if (leftNumber !== void 0) return -1;
|
|
45636
|
+
if (rightNumber !== void 0) return 1;
|
|
45637
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
45638
|
+
}
|
|
45438
45639
|
function compareVersions(left, right) {
|
|
45439
45640
|
const ours = parseVersion(left);
|
|
45440
45641
|
const theirs = parseVersion(right);
|
|
@@ -45452,7 +45653,8 @@ function compareVersions(left, right) {
|
|
|
45452
45653
|
const theirsPart = theirs.prerelease[index];
|
|
45453
45654
|
if (oursPart === void 0) return -1;
|
|
45454
45655
|
if (theirsPart === void 0) return 1;
|
|
45455
|
-
|
|
45656
|
+
const difference = comparePrereleaseIdentifier(oursPart, theirsPart);
|
|
45657
|
+
if (difference !== 0) return difference;
|
|
45456
45658
|
}
|
|
45457
45659
|
return 0;
|
|
45458
45660
|
}
|
|
@@ -45505,7 +45707,7 @@ function createUpdateChecker(deps) {
|
|
|
45505
45707
|
}
|
|
45506
45708
|
|
|
45507
45709
|
// src/update/version.ts
|
|
45508
|
-
var PLUGIN_VERSION = true ? "0.1.
|
|
45710
|
+
var PLUGIN_VERSION = true ? "0.1.44" : "0.0.0-dev";
|
|
45509
45711
|
|
|
45510
45712
|
// src/update/service.ts
|
|
45511
45713
|
import { TypertRemoteService as TypertRemoteService2 } from "@deepseek-ai/dsh-typert-protocol";
|
|
@@ -45533,56 +45735,6 @@ var OpencodeGoUpdateService = class extends TypertRemoteService2 {
|
|
|
45533
45735
|
}
|
|
45534
45736
|
};
|
|
45535
45737
|
|
|
45536
|
-
// src/usage/meter.ts
|
|
45537
|
-
var EMPTY3 = {
|
|
45538
|
-
calls: 0,
|
|
45539
|
-
inputTokens: 0,
|
|
45540
|
-
outputTokens: 0,
|
|
45541
|
-
cacheReadTokens: 0,
|
|
45542
|
-
cacheWriteTokens: 0,
|
|
45543
|
-
totalTokens: 0
|
|
45544
|
-
};
|
|
45545
|
-
function add(totals, usage) {
|
|
45546
|
-
const inputTokens = totals.inputTokens + usage.inputTokens;
|
|
45547
|
-
const outputTokens = totals.outputTokens + usage.outputTokens;
|
|
45548
|
-
const cacheReadTokens = totals.cacheReadTokens + (usage.cacheReadTokens ?? 0);
|
|
45549
|
-
const cacheWriteTokens = totals.cacheWriteTokens + (usage.cacheWriteTokens ?? 0);
|
|
45550
|
-
return {
|
|
45551
|
-
calls: totals.calls + 1,
|
|
45552
|
-
inputTokens,
|
|
45553
|
-
outputTokens,
|
|
45554
|
-
cacheReadTokens,
|
|
45555
|
-
cacheWriteTokens,
|
|
45556
|
-
// The four counts are disjoint (uncached input, cached input, output), so
|
|
45557
|
-
// their sum is the billed total. The provider's own `totalTokens` is not
|
|
45558
|
-
// used: it is optional, and mixing the two would double-count a cache read.
|
|
45559
|
-
totalTokens: inputTokens + outputTokens + cacheReadTokens + cacheWriteTokens
|
|
45560
|
-
};
|
|
45561
|
-
}
|
|
45562
|
-
var UsageMeter = class {
|
|
45563
|
-
sinceMs = Date.now();
|
|
45564
|
-
totals = EMPTY3;
|
|
45565
|
-
byModel = /* @__PURE__ */ new Map();
|
|
45566
|
-
/**
|
|
45567
|
-
* Record one completed call.
|
|
45568
|
-
* @param model - the model id the call was made with.
|
|
45569
|
-
* @param usage - the provider's own usage for that call.
|
|
45570
|
-
*/
|
|
45571
|
-
record(model, usage) {
|
|
45572
|
-
this.totals = add(this.totals, usage);
|
|
45573
|
-
this.byModel.set(model, add(this.byModel.get(model) ?? EMPTY3, usage));
|
|
45574
|
-
}
|
|
45575
|
-
/** The current reading, busiest model first. */
|
|
45576
|
-
snapshot(now = Date.now()) {
|
|
45577
|
-
return {
|
|
45578
|
-
sinceMs: this.sinceMs,
|
|
45579
|
-
atMs: now,
|
|
45580
|
-
totals: this.totals,
|
|
45581
|
-
models: [...this.byModel.entries()].map(([model, totals]) => ({ model, ...totals })).sort((left, right) => right.totalTokens - left.totalTokens || left.model.localeCompare(right.model))
|
|
45582
|
-
};
|
|
45583
|
-
}
|
|
45584
|
-
};
|
|
45585
|
-
|
|
45586
45738
|
// src/usage/windows.ts
|
|
45587
45739
|
import { attributionHeaders as attributionHeaders6, LlmError as LlmError10 } from "@deepseek-ai/dsh-llm";
|
|
45588
45740
|
var USAGE_MAX_BYTES = 1024 * 1024;
|
|
@@ -45697,6 +45849,37 @@ function apply(ctx, raw) {
|
|
|
45697
45849
|
const constant = live === void 0 ? PlainConfig(raw ?? {}) : void 0;
|
|
45698
45850
|
const current = () => live === void 0 ? constant : readConfig(live);
|
|
45699
45851
|
assertBaseURL(current().baseURL);
|
|
45852
|
+
const settingsNs = ctx.fiber?.entry?.options.id ?? name;
|
|
45853
|
+
let directory;
|
|
45854
|
+
let directoryError;
|
|
45855
|
+
let directoryRetired = false;
|
|
45856
|
+
const directoryEntry = (error) => ({
|
|
45857
|
+
provider: PROVIDER_ID,
|
|
45858
|
+
displayName: DISPLAY_NAME,
|
|
45859
|
+
settingsNs,
|
|
45860
|
+
// This plugin's profile is the entry's whole `config:` mapping — the
|
|
45861
|
+
// bundled `cordis.patch.yml` inserts `id: dshopencodego` with the route's
|
|
45862
|
+
// fields directly under `config`, and `src/config.ts` owns exactly those
|
|
45863
|
+
// fields — so there is no path from the section root to a nested profile.
|
|
45864
|
+
settingsPath: [],
|
|
45865
|
+
defaultBaseURL: DEFAULT_BASE_URL,
|
|
45866
|
+
...error === void 0 ? {} : { error }
|
|
45867
|
+
});
|
|
45868
|
+
const publishDirectory = (error) => {
|
|
45869
|
+
if (directoryRetired || directory !== void 0 && error === directoryError) return;
|
|
45870
|
+
directoryError = error;
|
|
45871
|
+
const entry = directoryEntry(error);
|
|
45872
|
+
try {
|
|
45873
|
+
if (directory === void 0) directory = ctx.llm.registerConfigurableProviders([entry]);
|
|
45874
|
+
else directory.replace([entry]);
|
|
45875
|
+
} catch (failure) {
|
|
45876
|
+
directoryRetired = true;
|
|
45877
|
+
ctx.logger.error(
|
|
45878
|
+
`dshopencodego: could not publish the "${PROVIDER_ID}" configurable-provider entry; the Models page will not offer this route's model discovery (${String(failure)})`
|
|
45879
|
+
);
|
|
45880
|
+
}
|
|
45881
|
+
};
|
|
45882
|
+
publishDirectory(void 0);
|
|
45700
45883
|
const resolveApiKey = async () => {
|
|
45701
45884
|
const ref = current().apiKeyEnv;
|
|
45702
45885
|
const credentials = ctx.get("credentials");
|
|
@@ -45740,7 +45923,10 @@ function apply(ctx, raw) {
|
|
|
45740
45923
|
});
|
|
45741
45924
|
ctx.plugin(OpencodeGoCatalogService, {
|
|
45742
45925
|
catalog: () => adapter.catalogOf(current()),
|
|
45743
|
-
visibility: () => current().modelVisibility
|
|
45926
|
+
visibility: () => current().modelVisibility,
|
|
45927
|
+
onUnadvertised: (ids) => {
|
|
45928
|
+
ctx.logger.warn(`dshopencodego: modelVisibility configures models the gateway does not advertise: ${ids.join(", ")}; those switches have no effect until the gateway publishes them`);
|
|
45929
|
+
}
|
|
45744
45930
|
});
|
|
45745
45931
|
ctx.plugin(OpencodeGoUpdateService, { currentVersion: PLUGIN_VERSION });
|
|
45746
45932
|
let registration;
|
|
@@ -45762,38 +45948,50 @@ function apply(ctx, raw) {
|
|
|
45762
45948
|
ctx.logger.info("dshopencodego: disabled by configuration; the route and its models are withdrawn");
|
|
45763
45949
|
}
|
|
45764
45950
|
}
|
|
45951
|
+
publishDirectory(enabled && !credentialConfigured ? `the route is withheld until ${current().apiKeyEnv} is stored through the credentials service` : void 0);
|
|
45765
45952
|
};
|
|
45766
45953
|
const syncRoute = () => {
|
|
45767
|
-
|
|
45768
|
-
|
|
45769
|
-
visibilityFacts
|
|
45770
|
-
|
|
45771
|
-
|
|
45772
|
-
|
|
45773
|
-
|
|
45774
|
-
|
|
45775
|
-
|
|
45776
|
-
|
|
45954
|
+
try {
|
|
45955
|
+
const visibility = JSON.stringify(current().modelVisibility);
|
|
45956
|
+
if (visibility !== visibilityFacts) {
|
|
45957
|
+
visibilityFacts = visibility;
|
|
45958
|
+
registration?.replace([PROVIDER_ID]);
|
|
45959
|
+
}
|
|
45960
|
+
const credentials = ctx.get("credentials");
|
|
45961
|
+
const ref = current().apiKeyEnv;
|
|
45962
|
+
if (credentials === void 0) {
|
|
45963
|
+
applyRoute(launchEnvironmentOf(ctx).get(ref)?.value !== void 0);
|
|
45964
|
+
return;
|
|
45965
|
+
}
|
|
45966
|
+
void credentials.describe(credentialRef(ref)).then((info) => {
|
|
45967
|
+
if (ref === current().apiKeyEnv) applyRoute(info.configured);
|
|
45968
|
+
}).catch((error) => {
|
|
45969
|
+
ctx.logger.error(`dshopencodego: credential describe failed; keeping the previous route state (${String(error)})`);
|
|
45970
|
+
});
|
|
45971
|
+
} catch (error) {
|
|
45972
|
+
ctx.logger.error(
|
|
45973
|
+
`dshopencodego: could not re-evaluate the route after a configuration change; keeping the previous route state (${String(error)})`
|
|
45974
|
+
);
|
|
45777
45975
|
}
|
|
45778
|
-
void credentials.describe(credentialRef(ref)).then((info) => {
|
|
45779
|
-
if (ref === current().apiKeyEnv) applyRoute(info.configured);
|
|
45780
|
-
}).catch((error) => {
|
|
45781
|
-
ctx.logger.error(`dshopencodego: credential describe failed; keeping the previous route state (${String(error)})`);
|
|
45782
|
-
});
|
|
45783
45976
|
};
|
|
45784
45977
|
syncRoute();
|
|
45785
|
-
const undiscover = ctx.llm.registerModelDiscovery(
|
|
45786
|
-
|
|
45787
|
-
|
|
45788
|
-
|
|
45789
|
-
|
|
45790
|
-
|
|
45978
|
+
const undiscover = ctx.llm.registerModelDiscovery(
|
|
45979
|
+
settingsNs,
|
|
45980
|
+
async (request, signal) => {
|
|
45981
|
+
if (request.provider !== PROVIDER_ID && !(request.baseURL ?? "").includes("opencode.ai")) {
|
|
45982
|
+
throw new LlmError12(
|
|
45983
|
+
"dshopencodego discovers only OpenCode Go/Console endpoints; enter this provider's models by hand",
|
|
45984
|
+
"DISCOVERY_UNSUPPORTED"
|
|
45985
|
+
);
|
|
45986
|
+
}
|
|
45987
|
+
return discoverCatalogModels(adapter.catalogOf(current()), signal);
|
|
45791
45988
|
}
|
|
45792
|
-
|
|
45793
|
-
});
|
|
45989
|
+
);
|
|
45794
45990
|
ctx.effect(() => () => {
|
|
45795
45991
|
registration?.();
|
|
45796
45992
|
undiscover();
|
|
45993
|
+
directory?.();
|
|
45994
|
+
directoryRetired = true;
|
|
45797
45995
|
});
|
|
45798
45996
|
ctx.on("internal/config", function(_raw, next) {
|
|
45799
45997
|
const value = next();
|