@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/types/adapter.d.ts
CHANGED
|
@@ -13,11 +13,12 @@
|
|
|
13
13
|
* @module @dan-ai-studio/dshopencodego/adapter
|
|
14
14
|
*/
|
|
15
15
|
import { attributionHeaders, LlmAdapter } from '@deepseek-ai/dsh-llm';
|
|
16
|
-
import type { GenerateOptions, ImageAttachmentAccess, LlmModelInfo, LlmResolvedModelInfo, PreparedAdapterCall, ResolvedRetryPolicy, StreamChunk
|
|
16
|
+
import type { GenerateOptions, ImageAttachmentAccess, LlmImageRequestPricing, LlmModelInfo, LlmResolvedModelInfo, PreparedAdapterCall, ResolvedRetryPolicy, StreamChunk } from '@deepseek-ai/dsh-llm';
|
|
17
17
|
import type { AttachmentStore, ImageAttachmentRef } from '@deepseek-ai/dsh-attachment';
|
|
18
18
|
import { OpencodeGoCatalog } from './catalog/index.ts';
|
|
19
19
|
import type { GoDoc } from './catalog/go-doc.ts';
|
|
20
20
|
import type { OpencodeGoConfig } from './config.ts';
|
|
21
|
+
import type { MeteredUsage } from './usage/meter.ts';
|
|
21
22
|
/** Image machinery the adapter reads once per request. */
|
|
22
23
|
export interface OpencodeGoImageAccess {
|
|
23
24
|
/** Resolve the durable attachment service at request time. */
|
|
@@ -54,11 +55,13 @@ export interface OpencodeGoAdapterOptions {
|
|
|
54
55
|
/**
|
|
55
56
|
* Observe the provider's own usage for one completed call. This is the only
|
|
56
57
|
* honest source of token counts: the gateway's `/usage` endpoint reports
|
|
57
|
-
* account percentages, not tokens.
|
|
58
|
+
* account percentages, not tokens. The value carries the rates that were
|
|
59
|
+
* published for the model when the call ran, so the meter can price it
|
|
60
|
+
* without ever looking a rate up after the fact.
|
|
58
61
|
*/
|
|
59
62
|
readonly onUsage?: (detail: {
|
|
60
63
|
model: string;
|
|
61
|
-
usage:
|
|
64
|
+
usage: MeteredUsage;
|
|
62
65
|
}) => void;
|
|
63
66
|
}
|
|
64
67
|
/**
|
|
@@ -78,6 +81,26 @@ export declare class OpencodeGoAdapter extends LlmAdapter {
|
|
|
78
81
|
id: string;
|
|
79
82
|
name: string;
|
|
80
83
|
};
|
|
84
|
+
/**
|
|
85
|
+
* This route declares no request-image pricing, and that is a decision
|
|
86
|
+
* rather than an omission.
|
|
87
|
+
*
|
|
88
|
+
* `priceImages` must answer with the visual tokens the wire actually carries
|
|
89
|
+
* for every image occurrence, and nothing available to this plugin can
|
|
90
|
+
* answer that. The route fronts several model families behind three
|
|
91
|
+
* protocols whose vision accounting differs and is not published by the
|
|
92
|
+
* gateway; the catalog carries text rates only (`ModelCost` is input,
|
|
93
|
+
* output, cache read and cache write) and states no vision rule; and the one
|
|
94
|
+
* implementation shipped upstream anchors on a single provider's own
|
|
95
|
+
* published accounting (`dsh-llm-deepseek`). An invented number would not
|
|
96
|
+
* fail loudly: the token meter sums these values into its surface estimate
|
|
97
|
+
* and the retention policy spends real budget against them, so a wrong
|
|
98
|
+
* answer is worse than the contract's own default — declaring none, which
|
|
99
|
+
* leaves every consumer on its neutral estimate. Revisit this once the
|
|
100
|
+
* gateway's per-family visual accounting is known and checked against the
|
|
101
|
+
* usage it reports.
|
|
102
|
+
*/
|
|
103
|
+
imageRequestPricing(_provider: string, _model: string): LlmImageRequestPricing | undefined;
|
|
81
104
|
listModels(_provider: string): Promise<readonly LlmModelInfo[]>;
|
|
82
105
|
resolveModel(_provider: string, model: string, _signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
|
|
83
106
|
/** Describe one model: capacities plus the reasoning levels it really offers. */
|
|
@@ -43,12 +43,16 @@ export interface GoDoc {
|
|
|
43
43
|
*/
|
|
44
44
|
export declare function parseGoDocument(markdown: string): GoDoc;
|
|
45
45
|
/**
|
|
46
|
-
* Fetch and parse the documentation,
|
|
46
|
+
* Fetch and parse the documentation, asking every source concurrently.
|
|
47
47
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
48
|
+
* The second URL is a CDN cache of the same file, so waiting for the first to
|
|
49
|
+
* fail before asking the second doubled the worst case on a cold process —
|
|
50
|
+
* 10 s per source — while the catalog build awaits this document only for the
|
|
51
|
+
* quota badge and the protocol fallback. `GO_DOC_URLS` order still decides:
|
|
52
|
+
* the repository file is examined before its cache mirror, and a source that
|
|
53
|
+
* fails is reported with its own reason.
|
|
54
|
+
* @param signal - caller cancellation; it reaches every source's fetch and
|
|
55
|
+
* aborts all of them, so a cancelled discovery stops waiting on the network.
|
|
52
56
|
* @returns the parsed document from the first source that carried one.
|
|
53
57
|
* @throws {LlmError} `DOCUMENT_UNAVAILABLE` when every source failed.
|
|
54
58
|
*/
|
|
@@ -76,9 +76,10 @@ export interface CatalogOptions {
|
|
|
76
76
|
readonly observers?: CatalogObservers;
|
|
77
77
|
/**
|
|
78
78
|
* Read the provider documentation. Defaults to the real fetch; tests inject a
|
|
79
|
-
* fixed document so the suite stays offline.
|
|
79
|
+
* fixed document so the suite stays offline. The signal is the caller's
|
|
80
|
+
* cancellation, forwarded to every documentation request.
|
|
80
81
|
*/
|
|
81
|
-
readonly readDocument?: () => Promise<GoDoc>;
|
|
82
|
+
readonly readDocument?: (signal?: AbortSignal) => Promise<GoDoc>;
|
|
82
83
|
}
|
|
83
84
|
/**
|
|
84
85
|
* One gateway's catalog. Requests reuse a cached snapshot; explicit discovery
|
|
@@ -94,20 +95,31 @@ export declare class OpencodeGoCatalog {
|
|
|
94
95
|
/** The last parsed documentation; retained across failed fetches. */
|
|
95
96
|
private document;
|
|
96
97
|
constructor(options: CatalogOptions);
|
|
97
|
-
/**
|
|
98
|
-
|
|
98
|
+
/**
|
|
99
|
+
* The cached snapshot, refreshed when it is older than the configured TTL.
|
|
100
|
+
* @param force - revalidate every source, ignoring the cache.
|
|
101
|
+
* @param signal - caller cancellation, forwarded to the documentation read,
|
|
102
|
+
* the slowest source and the only one a caller can cancel; a build already
|
|
103
|
+
* in flight keeps the signal of the caller that started it, because one
|
|
104
|
+
* shared snapshot cannot carry two different cancellations.
|
|
105
|
+
*/
|
|
106
|
+
snapshot(force?: boolean, signal?: AbortSignal): Promise<CatalogSnapshot>;
|
|
99
107
|
/**
|
|
100
108
|
* The snapshot that can serve one exact model.
|
|
101
109
|
*
|
|
102
110
|
* A model this build has never seen forces one revalidation even inside the
|
|
103
111
|
* TTL, because "unknown" and "newly added" look identical from here.
|
|
104
112
|
* @param id - the requested model id.
|
|
113
|
+
* @param signal - caller cancellation, forwarded to a forced revalidation.
|
|
105
114
|
* @returns the snapshot holding the model.
|
|
106
115
|
* @throws {LlmError} `MODEL_METADATA_UNAVAILABLE` when the gateway advertises
|
|
107
116
|
* the id but this build cannot configure it, naming the reason.
|
|
108
117
|
*/
|
|
109
|
-
forModel(id: string): Promise<CatalogSnapshot>;
|
|
110
|
-
/**
|
|
118
|
+
forModel(id: string, signal?: AbortSignal): Promise<CatalogSnapshot>;
|
|
119
|
+
/**
|
|
120
|
+
* Revalidate every source, reusing the metadata document when it is unchanged.
|
|
121
|
+
* @param signal - caller cancellation, forwarded to the documentation read.
|
|
122
|
+
*/
|
|
111
123
|
private build;
|
|
112
124
|
/** Merge the listing with the metadata document and the installed entries. */
|
|
113
125
|
private assemble;
|
|
@@ -131,8 +143,11 @@ export declare class OpencodeGoCatalog {
|
|
|
131
143
|
/**
|
|
132
144
|
* Interrogate the gateway for explicit discovery, bypassing the TTL.
|
|
133
145
|
* @param catalog - the catalog to revalidate.
|
|
146
|
+
* @param signal - the discovery request's cancellation. The harness states
|
|
147
|
+
* implementations "must honor the supplied signal", so it reaches the
|
|
148
|
+
* documentation fetch and a cancelled interrogation stops waiting on it.
|
|
134
149
|
* @returns the advertised models, including the unconfigured ones so the
|
|
135
150
|
* settings surface can explain what is missing.
|
|
136
151
|
* @throws {LlmError} `DISCOVERY_FAILED` when the listing itself failed.
|
|
137
152
|
*/
|
|
138
|
-
export declare function discoverCatalogModels(catalog: OpencodeGoCatalog): Promise<readonly LlmDiscoveredModel[]>;
|
|
153
|
+
export declare function discoverCatalogModels(catalog: OpencodeGoCatalog, signal?: AbortSignal): Promise<readonly LlmDiscoveredModel[]>;
|
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
* cache may answer. `refresh` is what the settings page's button calls, and it
|
|
6
6
|
* revalidates the gateway listing and the online metadata before projecting.
|
|
7
7
|
*
|
|
8
|
+
* A read that cannot be projected at all is this Remote's own domain failure:
|
|
9
|
+
* it carries a catalog code, not the usage Remote's, because the two describe
|
|
10
|
+
* different accounts of the world and a client branches on `code`.
|
|
11
|
+
*
|
|
8
12
|
* @module @dan-ai-studio/dshopencodego/catalog/service
|
|
9
13
|
*/
|
|
10
14
|
import type { Context } from '@deepseek-ai/cordis';
|
|
@@ -17,17 +21,56 @@ interface CatalogServiceOptions {
|
|
|
17
21
|
readonly catalog: () => OpencodeGoCatalog;
|
|
18
22
|
/** Current per-model visibility switches. */
|
|
19
23
|
readonly visibility: () => Readonly<Record<string, boolean>>;
|
|
24
|
+
/**
|
|
25
|
+
* Report configured visibility switches the gateway does not advertise,
|
|
26
|
+
* once per distinct set.
|
|
27
|
+
*/
|
|
28
|
+
readonly onUnadvertised?: (ids: readonly string[]) => void;
|
|
20
29
|
}
|
|
21
30
|
/** Catalog Remote: what the gateway serves, and how each model is called. */
|
|
22
31
|
export declare class OpencodeGoCatalogService extends TypertRemoteService {
|
|
23
32
|
private readonly options;
|
|
33
|
+
/** The last reported set of unadvertised switches, as a comparable fact. */
|
|
34
|
+
private reportedUnadvertised;
|
|
24
35
|
constructor(ctx: Context, options: CatalogServiceOptions);
|
|
25
36
|
/** The cached reading. */
|
|
26
37
|
read(): Promise<CatalogReading>;
|
|
27
38
|
/** Revalidate both sources, then read. */
|
|
28
39
|
refresh(): Promise<CatalogReading>;
|
|
29
40
|
private project;
|
|
41
|
+
/**
|
|
42
|
+
* Report switches for ids the gateway never advertised.
|
|
43
|
+
*
|
|
44
|
+
* `modelVisibility` is per-id configuration, and an entry naming a model the
|
|
45
|
+
* gateway does not serve has no effect anywhere — the picker cannot show a
|
|
46
|
+
* model that is not in the listing, so without this the switch is silent.
|
|
47
|
+
* (The reverse case, advertised but unbuildable, is `onUnconfigured`.)
|
|
48
|
+
*
|
|
49
|
+
* Only a live listing is evidence of absence: a retained snapshot after an
|
|
50
|
+
* outage would accuse models that merely could not be re-read. The set is
|
|
51
|
+
* compared as a fact, so re-reading the page does not repeat the warning.
|
|
52
|
+
* @param snapshot - the snapshot the reading was projected from.
|
|
53
|
+
* @param visibility - the switches in force for this read.
|
|
54
|
+
*/
|
|
55
|
+
private reportUnadvertised;
|
|
30
56
|
}
|
|
31
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* The domain failure the settings page renders when a refresh cannot run.
|
|
59
|
+
*
|
|
60
|
+
* Its code is the catalog's own: a client that branches on the usage code
|
|
61
|
+
* (`client/UsagePill.tsx:39`) must not read a catalog failure as a usage one.
|
|
62
|
+
* @param error - the failure the projection could not turn into a reading.
|
|
63
|
+
* @returns the retryable, retain-previous failure for the wire.
|
|
64
|
+
*/
|
|
32
65
|
export declare function catalogFailure(error: unknown): RemoteError;
|
|
66
|
+
declare module '@deepseek-ai/dsh-typert-protocol' {
|
|
67
|
+
interface RemoteErrorDetailsMap {
|
|
68
|
+
'dshopencodego/catalog-unavailable': {
|
|
69
|
+
/** Whether retrying the same read could succeed. */
|
|
70
|
+
readonly retryable: boolean;
|
|
71
|
+
/** Whether a previously displayed reading stays valid. */
|
|
72
|
+
readonly retainPrevious: boolean;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
33
76
|
export {};
|
package/lib/types/config.d.ts
CHANGED
|
@@ -92,9 +92,10 @@ export declare const Config: z<Partial<OpencodeGoConfig>, LiveConfig>;
|
|
|
92
92
|
*/
|
|
93
93
|
export declare function readConfig(config: LiveConfig): OpencodeGoConfig;
|
|
94
94
|
/**
|
|
95
|
-
* Accept only an http(s) base without a query or fragment
|
|
96
|
-
*
|
|
97
|
-
* written, never at first
|
|
95
|
+
* Accept only an http(s) base without a query or fragment, and only https
|
|
96
|
+
* unless the host is loopback. Runs at load for the composition layer and on
|
|
97
|
+
* every profile edit, so a bad URL fails where it is written, never at first
|
|
98
|
+
* request.
|
|
98
99
|
* @param raw - the configured base URL.
|
|
99
100
|
* @returns the normalized base URL without trailing slashes.
|
|
100
101
|
*/
|
|
@@ -24,6 +24,31 @@ export type PiAiReplayBlock = {
|
|
|
24
24
|
type: 'tool-call';
|
|
25
25
|
thoughtSignature?: string;
|
|
26
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* Stop reasons a durable envelope may carry.
|
|
29
|
+
*
|
|
30
|
+
* The read side accepts exactly this set, so the write side must never store
|
|
31
|
+
* anything outside it. `pending` and `deferred` are transport states, not
|
|
32
|
+
* durable facts: a pi-ai `done` event can carry `deferred`
|
|
33
|
+
* (`pi-ai/dist/types.d.ts:616`), and `mapStopReason` already renders both as
|
|
34
|
+
* an error finish (`./stream.ts:118-125`). Storing them verbatim made the next
|
|
35
|
+
* request read back its own envelope as invalid — the message degraded to
|
|
36
|
+
* provider-neutral history, every signature was dropped, and the plugin logged
|
|
37
|
+
* "unusable replay state".
|
|
38
|
+
*/
|
|
39
|
+
export declare const REPLAYABLE_STOP_REASONS: readonly ['stop', 'length', 'toolUse', 'error', 'aborted'];
|
|
40
|
+
/** One stop reason a replay envelope may carry. */
|
|
41
|
+
export type ReplayableStopReason = typeof REPLAYABLE_STOP_REASONS[number];
|
|
42
|
+
/**
|
|
43
|
+
* The durable form of a pi-ai stop reason.
|
|
44
|
+
*
|
|
45
|
+
* A non-terminal state (`pending`, `deferred`) is stored as `error`, matching
|
|
46
|
+
* the finish reason the stream reported for it: envelope and finish agree, and
|
|
47
|
+
* the message stays replayable.
|
|
48
|
+
* @param stopReason - the native stop reason of a terminal pi-ai message.
|
|
49
|
+
* @returns a reason the read side accepts.
|
|
50
|
+
*/
|
|
51
|
+
export declare function replayableStopReason(stopReason: AssistantMessage['stopReason']): ReplayableStopReason;
|
|
27
52
|
/** Versioned response-level half of the replay envelope. */
|
|
28
53
|
export interface PiAiReplayResponse {
|
|
29
54
|
kind: 'pi-ai';
|
|
@@ -37,10 +62,14 @@ export interface PiAiReplayResponse {
|
|
|
37
62
|
responseId?: string;
|
|
38
63
|
/** Provider-native effort, replayed as-is; absence stays absent. */
|
|
39
64
|
providerThinkingLevel?: string;
|
|
40
|
-
|
|
65
|
+
/** Durable terminal state; never a transport state like `pending`. */
|
|
66
|
+
stopReason: ReplayableStopReason;
|
|
41
67
|
}
|
|
42
68
|
/**
|
|
43
69
|
* Project a successful pi-ai response into the minimal durable replay state.
|
|
70
|
+
*
|
|
71
|
+
* The stop reason is stored in its durable form ({@link replayableStopReason}),
|
|
72
|
+
* so an envelope this process wrote is an envelope it can read back.
|
|
44
73
|
* @param message - the completed native response.
|
|
45
74
|
* @param requestedModel - request identity stored on the assistant source.
|
|
46
75
|
* @returns the versioned lossless-JSON envelope; `blocks` is index-aligned with
|
package/lib/types/models.d.ts
CHANGED
|
@@ -85,6 +85,20 @@ export declare function recommendedIds(models: readonly Pick<ModelSummary, 'id'
|
|
|
85
85
|
* @returns true when the model should be selectable.
|
|
86
86
|
*/
|
|
87
87
|
export declare function isModelEnabled(model: Pick<ModelSummary, 'id' | 'deprecated' | 'configurationMissing' | 'recommended'>, visibility?: Readonly<Record<string, boolean>>): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Configured visibility switches naming ids no source advertises.
|
|
90
|
+
*
|
|
91
|
+
* A switch exists to change one model's presence, so one for an id the
|
|
92
|
+
* gateway does not serve can only be inert — nothing else in the plugin can
|
|
93
|
+
* report it, because the model is absent from every list a surface renders.
|
|
94
|
+
* The reverse case (advertised but unbuildable) is reported where the catalog
|
|
95
|
+
* assembles; this is the diagnostic for the other direction.
|
|
96
|
+
* @param visibility - the configured per-model switches.
|
|
97
|
+
* @param advertised - every id the gateway currently advertises, whether or
|
|
98
|
+
* not this build can configure it.
|
|
99
|
+
* @returns the configured ids with no advertised counterpart, sorted.
|
|
100
|
+
*/
|
|
101
|
+
export declare function unadvertisedVisibilityIds(visibility: Readonly<Record<string, boolean>>, advertised: Iterable<string>): string[];
|
|
88
102
|
/** A calendar date as models.dev states it, without a timezone. */
|
|
89
103
|
export declare function validReleaseDate(value: unknown): value is string;
|
|
90
104
|
/**
|
|
@@ -7,12 +7,74 @@
|
|
|
7
7
|
* per model and in total, for the lifetime of the process — and says so: the
|
|
8
8
|
* number is labelled "since this Harness started", never "today".
|
|
9
9
|
*
|
|
10
|
+
* It also multiplies those counts by the rates the catalog published for the
|
|
11
|
+
* model, because a token count is not what a user is looking for. The product
|
|
12
|
+
* of the two is an *estimate*: the rates come from models.dev, the counts come
|
|
13
|
+
* from the provider, and on the subscription endpoints the account is not
|
|
14
|
+
* billed per token at all. The meter therefore never invents a rate — a model
|
|
15
|
+
* the catalog left unpriced is reported as unpriced, and its tokens are
|
|
16
|
+
* reported separately rather than folded into the estimate as zero.
|
|
17
|
+
*
|
|
10
18
|
* A conversation's own totals are durable and are folded from the session log
|
|
11
19
|
* by the client, which is why nothing here persists.
|
|
12
20
|
*
|
|
13
21
|
* @module @dan-ai-studio/dshopencodego/usage/meter
|
|
14
22
|
*/
|
|
15
23
|
import type { TokenUsage } from '@deepseek-ai/dsh-llm';
|
|
24
|
+
/**
|
|
25
|
+
* Per-million-token rates for one model: models.dev's own unit, kept as-is so
|
|
26
|
+
* no rounding is introduced between the published document and the meter.
|
|
27
|
+
*/
|
|
28
|
+
export interface MeterRates {
|
|
29
|
+
readonly input: number;
|
|
30
|
+
readonly output: number;
|
|
31
|
+
/**
|
|
32
|
+
* Absent when the catalog states no positive cache rate. Cache traffic is
|
|
33
|
+
* then estimated at the input rate rather than at zero — see
|
|
34
|
+
* {@link spendOf}.
|
|
35
|
+
*/
|
|
36
|
+
readonly cacheRead?: number;
|
|
37
|
+
readonly cacheWrite?: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The rate block exactly as a catalog entry carries it: four numbers, where a
|
|
41
|
+
* zero means "the sources state no positive rate" rather than "free"
|
|
42
|
+
* (see `catalogReading`, which omits `cost` under the same rule).
|
|
43
|
+
*/
|
|
44
|
+
export interface CatalogRates {
|
|
45
|
+
readonly input: number;
|
|
46
|
+
readonly output: number;
|
|
47
|
+
readonly cacheRead: number;
|
|
48
|
+
readonly cacheWrite: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Accept a catalog rate block only when it states a positive rate.
|
|
52
|
+
*
|
|
53
|
+
* A block of zeros is what both "models.dev has no entry for this id" and
|
|
54
|
+
* "models.dev states no price" look like by the time the adapter sees them, and
|
|
55
|
+
* pricing tokens at those zeros would render an unknown model as free. The
|
|
56
|
+
* cache fields are dropped when they are not positive for the same reason:
|
|
57
|
+
* absent means "not published", which {@link spendOf} resolves to the input
|
|
58
|
+
* rate instead of to zero.
|
|
59
|
+
* @param cost - the model's rate block, or undefined when it has none.
|
|
60
|
+
* @returns the usable rates, or undefined when the block publishes no price.
|
|
61
|
+
*/
|
|
62
|
+
export declare function usableRates(cost: CatalogRates | undefined): MeterRates | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* One completed call: the provider's own counts, plus the rates that were in
|
|
65
|
+
* force for the model when the call was made.
|
|
66
|
+
*
|
|
67
|
+
* The rates ride on this value instead of a second argument because the mount
|
|
68
|
+
* hands the adapter's observation straight to {@link UsageMeter.record}
|
|
69
|
+
* (`onUsage: ({ model, usage }) => meter.record(model, usage)`), so a second
|
|
70
|
+
* channel would need a second parameter the mount does not pass. Rates are
|
|
71
|
+
* captured per call rather than looked up later, so a catalog refresh can never
|
|
72
|
+
* re-price tokens that were already spent.
|
|
73
|
+
*/
|
|
74
|
+
export interface MeteredUsage extends TokenUsage {
|
|
75
|
+
/** Rates published for this model at call time; absent when it had none. */
|
|
76
|
+
readonly rates?: MeterRates;
|
|
77
|
+
}
|
|
16
78
|
/** Token counts and call count for one model, or for every model. */
|
|
17
79
|
export interface MeterTotals {
|
|
18
80
|
readonly calls: number;
|
|
@@ -22,6 +84,30 @@ export interface MeterTotals {
|
|
|
22
84
|
readonly cacheWriteTokens: number;
|
|
23
85
|
/** Sum of the four token fields, which is what a provider bills. */
|
|
24
86
|
readonly totalTokens: number;
|
|
87
|
+
/**
|
|
88
|
+
* Reasoning/thinking tokens the provider reported. This is a *subset* of
|
|
89
|
+
* `outputTokens` — pi-ai documents `Usage.reasoning` as included in
|
|
90
|
+
* `output`, and both of its wire parsers fold it in (OpenAI's
|
|
91
|
+
* `completion_tokens` and Anthropic's `output_tokens` already contain it) —
|
|
92
|
+
* so it is counted here and deliberately kept out of `totalTokens`, which
|
|
93
|
+
* would otherwise bill the same tokens twice. Absent until a call reports a
|
|
94
|
+
* positive count; providers that publish no breakdown leave it out.
|
|
95
|
+
*/
|
|
96
|
+
readonly reasoningTokens?: number;
|
|
97
|
+
/**
|
|
98
|
+
* Estimated USD at the rates in force for each priced call, covering only
|
|
99
|
+
* the tokens those rates priced — `unpricedTokens` names the rest. Absent
|
|
100
|
+
* while no call has had a rate; never a zero standing in for "unknown",
|
|
101
|
+
* because a zero would read as "free".
|
|
102
|
+
*/
|
|
103
|
+
readonly estimatedUSD?: number;
|
|
104
|
+
/**
|
|
105
|
+
* Tokens the estimate above does not cover, named so a partial figure is
|
|
106
|
+
* never mistaken for the whole spend. Present only alongside an estimate:
|
|
107
|
+
* with no estimate at all there is nothing to qualify, and the reading
|
|
108
|
+
* already says "unpriced" by omitting `estimatedUSD`.
|
|
109
|
+
*/
|
|
110
|
+
readonly unpricedTokens?: number;
|
|
25
111
|
}
|
|
26
112
|
/** One model's share of the since-boot meter. */
|
|
27
113
|
export interface MeterModelEntry extends MeterTotals {
|
|
@@ -45,9 +131,10 @@ export declare class UsageMeter {
|
|
|
45
131
|
/**
|
|
46
132
|
* Record one completed call.
|
|
47
133
|
* @param model - the model id the call was made with.
|
|
48
|
-
* @param usage - the provider's own usage for that call
|
|
134
|
+
* @param usage - the provider's own usage for that call, with the rates that
|
|
135
|
+
* were published for the model at the time it ran.
|
|
49
136
|
*/
|
|
50
|
-
record(model: string, usage:
|
|
137
|
+
record(model: string, usage: MeteredUsage): void;
|
|
51
138
|
/** The current reading, busiest model first. */
|
|
52
139
|
snapshot(now?: number): GoMeter;
|
|
53
140
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dan-ai-studio/dshopencodego",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.44",
|
|
4
4
|
"description": "OpenCode Go provider for DeepSeek Harness: live gateway catalog, per-conversation session header, usage",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"author": "dan ai studio",
|
|
7
8
|
"icon": "./icon.svg",
|
|
8
9
|
"repository": {
|
|
9
10
|
"type": "git",
|
|
@@ -15,7 +16,7 @@
|
|
|
15
16
|
},
|
|
16
17
|
"engines": {
|
|
17
18
|
"node": "^22.19.0 || >=24.0.0",
|
|
18
|
-
"dsh": ">=0.2.0-rc.1 <0.3.0"
|
|
19
|
+
"dsh": ">=0.2.0-rc.1 <0.3.0-0"
|
|
19
20
|
},
|
|
20
21
|
"main": "./lib/index.js",
|
|
21
22
|
"types": "./lib/types/index.d.ts",
|
|
@@ -25,7 +26,6 @@
|
|
|
25
26
|
"default": "./lib/index.js"
|
|
26
27
|
},
|
|
27
28
|
"./client": {
|
|
28
|
-
"types": "./lib/types/client/index.d.ts",
|
|
29
29
|
"default": "./lib/client.js"
|
|
30
30
|
},
|
|
31
31
|
"./locale/*.json": "./locale/*.json",
|
|
@@ -65,28 +65,27 @@
|
|
|
65
65
|
"test": "vitest run",
|
|
66
66
|
"test:coverage": "vitest run --coverage",
|
|
67
67
|
"test:watch": "vitest",
|
|
68
|
-
"prepare": "npm run build",
|
|
69
68
|
"prepack": "npm run build"
|
|
70
69
|
},
|
|
71
70
|
"peerDependencies": {
|
|
72
71
|
"@deepseek-ai/cordis": "4.0.4 || 4.0.5-alpha.1 || 4.0.5-alpha.2 || 4.0.5",
|
|
73
|
-
"@deepseek-ai/dsh-api-remotes": ">=0.2.0-rc.1 <0.3.0",
|
|
74
|
-
"@deepseek-ai/dsh-attachment": ">=0.2.0-rc.1 <0.3.0",
|
|
75
|
-
"@deepseek-ai/dsh-brand": ">=0.2.0-rc.1 <0.3.0",
|
|
76
|
-
"@deepseek-ai/dsh-client-locale": ">=0.2.0-rc.1 <0.3.0",
|
|
77
|
-
"@deepseek-ai/dsh-client-store": ">=0.2.0-rc.1 <0.3.0",
|
|
78
|
-
"@deepseek-ai/dsh-client-ui-conversation": ">=0.2.0-rc.1 <0.3.0",
|
|
79
|
-
"@deepseek-ai/dsh-client-ui-model-selection": ">=0.2.0-rc.1 <0.3.0",
|
|
80
|
-
"@deepseek-ai/dsh-client-ui-renderer": ">=0.2.0-rc.1 <0.3.0",
|
|
81
|
-
"@deepseek-ai/dsh-client-ui-settings": ">=0.2.0-rc.1 <0.3.0",
|
|
82
|
-
"@deepseek-ai/dsh-client-ui-slots": ">=0.2.0-rc.1 <0.3.0",
|
|
83
|
-
"@deepseek-ai/dsh-credentials": ">=0.2.0-rc.1 <0.3.0",
|
|
84
|
-
"@deepseek-ai/dsh-fs": ">=0.2.0-rc.1 <0.3.0",
|
|
85
|
-
"@deepseek-ai/dsh-launch-environment": ">=0.2.0-rc.1 <0.3.0",
|
|
86
|
-
"@deepseek-ai/dsh-llm": ">=0.2.0-rc.1 <0.3.0",
|
|
87
|
-
"@deepseek-ai/dsh-timeout": ">=0.2.0-rc.1 <0.3.0",
|
|
88
|
-
"@deepseek-ai/dsh-typert-protocol": ">=0.2.0-rc.1 <0.3.0",
|
|
89
|
-
"@deepseek-ai/dsh-typert-registry": ">=0.2.0-rc.1 <0.3.0"
|
|
72
|
+
"@deepseek-ai/dsh-api-remotes": ">=0.2.0-rc.1 <0.3.0-0",
|
|
73
|
+
"@deepseek-ai/dsh-attachment": ">=0.2.0-rc.1 <0.3.0-0",
|
|
74
|
+
"@deepseek-ai/dsh-brand": ">=0.2.0-rc.1 <0.3.0-0",
|
|
75
|
+
"@deepseek-ai/dsh-client-locale": ">=0.2.0-rc.1 <0.3.0-0",
|
|
76
|
+
"@deepseek-ai/dsh-client-store": ">=0.2.0-rc.1 <0.3.0-0",
|
|
77
|
+
"@deepseek-ai/dsh-client-ui-conversation": ">=0.2.0-rc.1 <0.3.0-0",
|
|
78
|
+
"@deepseek-ai/dsh-client-ui-model-selection": ">=0.2.0-rc.1 <0.3.0-0",
|
|
79
|
+
"@deepseek-ai/dsh-client-ui-renderer": ">=0.2.0-rc.1 <0.3.0-0",
|
|
80
|
+
"@deepseek-ai/dsh-client-ui-settings": ">=0.2.0-rc.1 <0.3.0-0",
|
|
81
|
+
"@deepseek-ai/dsh-client-ui-slots": ">=0.2.0-rc.1 <0.3.0-0",
|
|
82
|
+
"@deepseek-ai/dsh-credentials": ">=0.2.0-rc.1 <0.3.0-0",
|
|
83
|
+
"@deepseek-ai/dsh-fs": ">=0.2.0-rc.1 <0.3.0-0",
|
|
84
|
+
"@deepseek-ai/dsh-launch-environment": ">=0.2.0-rc.1 <0.3.0-0",
|
|
85
|
+
"@deepseek-ai/dsh-llm": ">=0.2.0-rc.1 <0.3.0-0",
|
|
86
|
+
"@deepseek-ai/dsh-timeout": ">=0.2.0-rc.1 <0.3.0-0",
|
|
87
|
+
"@deepseek-ai/dsh-typert-protocol": ">=0.2.0-rc.1 <0.3.0-0",
|
|
88
|
+
"@deepseek-ai/dsh-typert-registry": ">=0.2.0-rc.1 <0.3.0-0"
|
|
90
89
|
},
|
|
91
90
|
"peerDependenciesMeta": {
|
|
92
91
|
"@deepseek-ai/dsh-api-remotes": {
|