@dereekb/openrouter 13.37.0
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/LICENSE +21 -0
- package/README.md +195 -0
- package/firebase/index.cjs.default.js +1 -0
- package/firebase/index.cjs.js +666 -0
- package/firebase/index.cjs.mjs +2 -0
- package/firebase/index.d.ts +1 -0
- package/firebase/index.esm.js +626 -0
- package/firebase/package.json +25 -0
- package/firebase/src/index.d.ts +1 -0
- package/firebase/src/lib/index.d.ts +4 -0
- package/firebase/src/lib/openrouter.api.d.ts +226 -0
- package/firebase/src/lib/openrouter.id.d.ts +56 -0
- package/firebase/src/lib/openrouter.model.d.ts +609 -0
- package/firebase/src/lib/openrouter.query.d.ts +121 -0
- package/firebase-server/index.cjs.default.js +1 -0
- package/firebase-server/index.cjs.js +4520 -0
- package/firebase-server/index.cjs.mjs +2 -0
- package/firebase-server/index.d.ts +1 -0
- package/firebase-server/index.esm.js +4466 -0
- package/firebase-server/package.json +38 -0
- package/firebase-server/src/index.d.ts +1 -0
- package/firebase-server/src/lib/index.d.ts +10 -0
- package/firebase-server/src/lib/openrouter.action.server.d.ts +196 -0
- package/firebase-server/src/lib/openrouter.broadcast.d.ts +93 -0
- package/firebase-server/src/lib/openrouter.call.inline.d.ts +57 -0
- package/firebase-server/src/lib/openrouter.file.attachment.d.ts +97 -0
- package/firebase-server/src/lib/openrouter.module.d.ts +65 -0
- package/firebase-server/src/lib/openrouter.prompt.service.d.ts +109 -0
- package/firebase-server/src/lib/openrouter.runtask.handle.d.ts +56 -0
- package/firebase-server/src/lib/openrouter.runtask.service.d.ts +380 -0
- package/firebase-server/src/lib/openrouter.runtask.sweep.d.ts +170 -0
- package/firebase-server/src/lib/openrouter.state.accessor.d.ts +106 -0
- package/firebase-server/src/test/openrouter.fake.d.ts +134 -0
- package/index.cjs.default.js +1 -0
- package/index.cjs.js +1867 -0
- package/index.cjs.mjs +2 -0
- package/index.d.ts +1 -0
- package/index.esm.js +1771 -0
- package/package.json +32 -0
- package/src/index.d.ts +1 -0
- package/src/lib/index.d.ts +10 -0
- package/src/lib/openrouter.call.d.ts +268 -0
- package/src/lib/openrouter.config.d.ts +314 -0
- package/src/lib/openrouter.embedding.d.ts +87 -0
- package/src/lib/openrouter.generation.d.ts +46 -0
- package/src/lib/openrouter.input.d.ts +238 -0
- package/src/lib/openrouter.prompt.d.ts +79 -0
- package/src/lib/openrouter.request.d.ts +91 -0
- package/src/lib/openrouter.sdk.d.ts +37 -0
- package/src/lib/openrouter.tool.d.ts +99 -0
- package/src/lib/openrouter.type.d.ts +125 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { type Maybe } from '@dereekb/util';
|
|
2
|
+
import { type OpenRouterProviderConfig } from './openrouter.config';
|
|
3
|
+
import { type OpenRouterCore, type RequestOptions } from './openrouter.sdk';
|
|
4
|
+
import { type OpenRouterModelId } from './openrouter.type';
|
|
5
|
+
/**
|
|
6
|
+
* Params for {@link openRouterEmbeddings}.
|
|
7
|
+
*/
|
|
8
|
+
export interface OpenRouterEmbeddingsParams {
|
|
9
|
+
/**
|
|
10
|
+
* The OpenRouter client.
|
|
11
|
+
*/
|
|
12
|
+
readonly client: OpenRouterCore;
|
|
13
|
+
/**
|
|
14
|
+
* The embeddings model to use.
|
|
15
|
+
*/
|
|
16
|
+
readonly model: OpenRouterModelId;
|
|
17
|
+
/**
|
|
18
|
+
* Text(s) to embed.
|
|
19
|
+
*/
|
|
20
|
+
readonly input: string | string[];
|
|
21
|
+
/**
|
|
22
|
+
* Output dimensionality, when the model supports reducing it.
|
|
23
|
+
*/
|
|
24
|
+
readonly dimensions?: Maybe<number>;
|
|
25
|
+
/**
|
|
26
|
+
* Input type hint (e.g. `search_query`, `search_document`) for models that distinguish them.
|
|
27
|
+
*/
|
|
28
|
+
readonly inputType?: Maybe<string>;
|
|
29
|
+
/**
|
|
30
|
+
* Provider routing — the same pinning that matters everywhere else applies here.
|
|
31
|
+
*/
|
|
32
|
+
readonly provider?: Maybe<OpenRouterProviderConfig>;
|
|
33
|
+
/**
|
|
34
|
+
* Additional request options.
|
|
35
|
+
*/
|
|
36
|
+
readonly options?: Maybe<RequestOptions>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* One embedding vector.
|
|
40
|
+
*/
|
|
41
|
+
export interface OpenRouterEmbedding {
|
|
42
|
+
/**
|
|
43
|
+
* Index of this embedding in the input list.
|
|
44
|
+
*/
|
|
45
|
+
readonly index: number;
|
|
46
|
+
/**
|
|
47
|
+
* The vector.
|
|
48
|
+
*/
|
|
49
|
+
readonly embedding: number[];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Result of an embeddings request.
|
|
53
|
+
*/
|
|
54
|
+
export interface OpenRouterEmbeddingsResult {
|
|
55
|
+
readonly model: string;
|
|
56
|
+
readonly embeddings: OpenRouterEmbedding[];
|
|
57
|
+
readonly promptTokens?: Maybe<number>;
|
|
58
|
+
readonly totalTokens?: Maybe<number>;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Generates embeddings.
|
|
62
|
+
*
|
|
63
|
+
* Base64-encoded embeddings are decoded to numbers before being returned, so a caller never has to
|
|
64
|
+
* branch on the encoding the model happened to use.
|
|
65
|
+
*
|
|
66
|
+
* @param params - The client, model, input, and routing options.
|
|
67
|
+
* @returns The embeddings.
|
|
68
|
+
*/
|
|
69
|
+
export declare function openRouterEmbeddings(params: OpenRouterEmbeddingsParams): Promise<OpenRouterEmbeddingsResult>;
|
|
70
|
+
/**
|
|
71
|
+
* Decodes a base64 string to bytes.
|
|
72
|
+
*
|
|
73
|
+
* `atob` is a WHATWG global, present on Node ≥16 and in every browser, so no Node-only `Buffer` is assumed.
|
|
74
|
+
* Anything outside the base64 alphabet — padding, embedded newlines from a wrapped response — is stripped
|
|
75
|
+
* first, which is what makes the input tolerant rather than strict.
|
|
76
|
+
*
|
|
77
|
+
* @param base64 - The base64 string. Padding is optional.
|
|
78
|
+
* @returns The decoded bytes.
|
|
79
|
+
*/
|
|
80
|
+
export declare function openRouterDecodeBase64(base64: string): Uint8Array;
|
|
81
|
+
/**
|
|
82
|
+
* Normalizes an embedding to a number array, decoding the base64 (little-endian float32) form.
|
|
83
|
+
*
|
|
84
|
+
* @param embedding - The embedding as returned.
|
|
85
|
+
* @returns The vector.
|
|
86
|
+
*/
|
|
87
|
+
export declare function openRouterEmbeddingVector(embedding: number[] | string): number[];
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type Maybe } from '@dereekb/util';
|
|
2
|
+
import { type GenerationContentData, type GenerationResponseData, type OpenRouterCore, type RequestOptions } from './openrouter.sdk';
|
|
3
|
+
import { type OpenRouterGenerationId } from './openrouter.type';
|
|
4
|
+
/**
|
|
5
|
+
* Params for {@link openRouterGeneration} and {@link openRouterGenerationContent}.
|
|
6
|
+
*/
|
|
7
|
+
export interface OpenRouterGenerationParams {
|
|
8
|
+
/**
|
|
9
|
+
* The OpenRouter client.
|
|
10
|
+
*/
|
|
11
|
+
readonly client: OpenRouterCore;
|
|
12
|
+
/**
|
|
13
|
+
* The generation id, as stored on a run task's `gi`.
|
|
14
|
+
*/
|
|
15
|
+
readonly id: OpenRouterGenerationId;
|
|
16
|
+
/**
|
|
17
|
+
* Additional request options.
|
|
18
|
+
*/
|
|
19
|
+
readonly options?: Maybe<RequestOptions>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Loads a generation's metadata — finish reason, cancellation, BYOK, latency, and the full token/cost
|
|
23
|
+
* breakdown, which is finalised server-side and so can be more complete than the usage the response
|
|
24
|
+
* carried.
|
|
25
|
+
*
|
|
26
|
+
* This is an AUDIT surface, never the system of record. What it returns is tied to account logging
|
|
27
|
+
* settings (nothing is retained under ZDR / logging-disabled), its retention is undocumented, and it is
|
|
28
|
+
* keyed per generation rather than per conversation — which is why a run task stores its own output and
|
|
29
|
+
* keeps `gi (generationIds)` only for lookups like this one.
|
|
30
|
+
*
|
|
31
|
+
* @param params - The client and generation id.
|
|
32
|
+
* @returns The generation metadata.
|
|
33
|
+
* @throws {Error} When the lookup fails, including the 404 a generation that was never retained produces.
|
|
34
|
+
*/
|
|
35
|
+
export declare function openRouterGeneration(params: OpenRouterGenerationParams): Promise<GenerationResponseData>;
|
|
36
|
+
/**
|
|
37
|
+
* Loads a generation's stored content — prompt, completion, reasoning, and output.
|
|
38
|
+
*
|
|
39
|
+
* Subject to every caveat on {@link openRouterGeneration}: this reloads what OpenRouter happened to
|
|
40
|
+
* retain, not what the run produced.
|
|
41
|
+
*
|
|
42
|
+
* @param params - The client and generation id.
|
|
43
|
+
* @returns The stored content.
|
|
44
|
+
* @throws {Error} When the lookup fails.
|
|
45
|
+
*/
|
|
46
|
+
export declare function openRouterGenerationContent(params: OpenRouterGenerationParams): Promise<GenerationContentData>;
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { type Maybe } from '@dereekb/util';
|
|
2
|
+
import { type OpenRouterFileAnnotation, type OpenRouterFileReference } from './openrouter.type';
|
|
3
|
+
/**
|
|
4
|
+
* Role of an input message.
|
|
5
|
+
*/
|
|
6
|
+
export type OpenRouterInputRole = 'user' | 'system' | 'assistant' | 'developer';
|
|
7
|
+
/**
|
|
8
|
+
* A text content part.
|
|
9
|
+
*/
|
|
10
|
+
export interface OpenRouterInputTextPart {
|
|
11
|
+
readonly type: 'input_text';
|
|
12
|
+
readonly text: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* An image content part.
|
|
16
|
+
*
|
|
17
|
+
* Images go in as ordinary image parts — there is no need to wrap one in a PDF the way OpenAI's
|
|
18
|
+
* background-mode file bug forced.
|
|
19
|
+
*/
|
|
20
|
+
export interface OpenRouterInputImagePart {
|
|
21
|
+
readonly type: 'input_image';
|
|
22
|
+
readonly imageUrl: string;
|
|
23
|
+
readonly detail: 'auto' | 'low' | 'high' | 'original';
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A file content part.
|
|
27
|
+
*
|
|
28
|
+
* OpenRouter takes the file inline in the message — there is no upload step and nothing to clean up
|
|
29
|
+
* afterwards. `fileUrl` is preferred (OpenRouter's docs: "Send publicly accessible PDFs directly
|
|
30
|
+
* without downloading or encoding"); `fileData` is the base64 fallback for an object we will not
|
|
31
|
+
* expose by URL, at the cost of bloating both the request and the stored run task.
|
|
32
|
+
*
|
|
33
|
+
* `fileId` (the OpenRouter Files blob store) is deliberately not modeled: it reintroduces exactly the
|
|
34
|
+
* "files live on their servers and are never deleted" problem this package removes.
|
|
35
|
+
*/
|
|
36
|
+
export interface OpenRouterInputFilePart {
|
|
37
|
+
readonly type: 'input_file';
|
|
38
|
+
readonly fileUrl?: Maybe<string>;
|
|
39
|
+
readonly fileData?: Maybe<string>;
|
|
40
|
+
readonly filename?: Maybe<string>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Any input content part.
|
|
44
|
+
*/
|
|
45
|
+
export type OpenRouterInputContentPart = OpenRouterInputTextPart | OpenRouterInputImagePart | OpenRouterInputFilePart;
|
|
46
|
+
/**
|
|
47
|
+
* A single input message.
|
|
48
|
+
*/
|
|
49
|
+
export interface OpenRouterInputMessage {
|
|
50
|
+
readonly role: OpenRouterInputRole;
|
|
51
|
+
readonly content: string | OpenRouterInputContentPart[];
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Input to a request: a bare string (shorthand for a single user message) or a list of messages.
|
|
55
|
+
*/
|
|
56
|
+
export type OpenRouterInput = string | OpenRouterInputMessage[];
|
|
57
|
+
/**
|
|
58
|
+
* Normalizes input to a message array.
|
|
59
|
+
*
|
|
60
|
+
* @param input - The input to normalize.
|
|
61
|
+
* @returns The messages, empty when no input was given.
|
|
62
|
+
*/
|
|
63
|
+
export declare function openRouterInputMessages(input: Maybe<OpenRouterInput>): OpenRouterInputMessage[];
|
|
64
|
+
/**
|
|
65
|
+
* Builds a text content part.
|
|
66
|
+
*
|
|
67
|
+
* @param text - The text.
|
|
68
|
+
* @returns The content part.
|
|
69
|
+
*
|
|
70
|
+
* @__NO_SIDE_EFFECTS__
|
|
71
|
+
*/
|
|
72
|
+
export declare function openRouterInputTextPart(text: string): OpenRouterInputTextPart;
|
|
73
|
+
/**
|
|
74
|
+
* Builds an image content part.
|
|
75
|
+
*
|
|
76
|
+
* @param imageUrl - The image url (a signed url, a data url, or any publicly reachable url).
|
|
77
|
+
* @param detail - Detail level. Defaults to `auto`.
|
|
78
|
+
* @returns The content part.
|
|
79
|
+
*
|
|
80
|
+
* @__NO_SIDE_EFFECTS__
|
|
81
|
+
*/
|
|
82
|
+
export declare function openRouterInputImagePart(imageUrl: string, detail?: OpenRouterInputImagePart['detail']): OpenRouterInputImagePart;
|
|
83
|
+
/**
|
|
84
|
+
* Builds a file content part from a url.
|
|
85
|
+
*
|
|
86
|
+
* @param fileUrl - The url the parser will dereference. Must be reachable from the public internet.
|
|
87
|
+
* @param filename - Filename to present; its extension is what tells OpenRouter how to treat the file.
|
|
88
|
+
* @returns The content part.
|
|
89
|
+
*
|
|
90
|
+
* @__NO_SIDE_EFFECTS__
|
|
91
|
+
*/
|
|
92
|
+
export declare function openRouterInputFileUrlPart(fileUrl: string, filename: string): OpenRouterInputFilePart;
|
|
93
|
+
/**
|
|
94
|
+
* Builds a file content part from base64 data.
|
|
95
|
+
*
|
|
96
|
+
* @param base64 - Base64 content, with or without a `data:` prefix.
|
|
97
|
+
* @param filename - Filename to present.
|
|
98
|
+
* @param contentType - Mime type used to build the `data:` prefix when the input lacks one. Defaults to `application/pdf`.
|
|
99
|
+
* @returns The content part.
|
|
100
|
+
*
|
|
101
|
+
* @__NO_SIDE_EFFECTS__
|
|
102
|
+
*/
|
|
103
|
+
export declare function openRouterInputFileDataPart(base64: string, filename: string, contentType?: string): OpenRouterInputFilePart;
|
|
104
|
+
/**
|
|
105
|
+
* A file reference paired with however it is being carried on THIS attempt.
|
|
106
|
+
*
|
|
107
|
+
* Exactly one of `fileUrl` / `fileData` is expected. Which one depends on whether the object is
|
|
108
|
+
* reachable from the public internet: a signed url is cheap and keeps the request small, and inline
|
|
109
|
+
* base64 is the fallback for an object OpenRouter cannot dereference — most notably anything living in
|
|
110
|
+
* the Firebase storage emulator, where "signed" urls point at localhost.
|
|
111
|
+
*/
|
|
112
|
+
export interface OpenRouterAttachedFileReference {
|
|
113
|
+
readonly file: OpenRouterFileReference;
|
|
114
|
+
/**
|
|
115
|
+
* The signed url, minted for THIS attempt.
|
|
116
|
+
*
|
|
117
|
+
* Never persist this — see {@link OpenRouterFileReference} for why a url cannot outlive the attempt that
|
|
118
|
+
* minted it.
|
|
119
|
+
*/
|
|
120
|
+
readonly fileUrl?: Maybe<string>;
|
|
121
|
+
/**
|
|
122
|
+
* A `data:<mime>;base64,…` url carrying the file inline on THIS attempt.
|
|
123
|
+
*
|
|
124
|
+
* Never persist this either, for a different reason: it is the whole file, re-read on every attempt,
|
|
125
|
+
* and a run task document has a 1 MiB ceiling.
|
|
126
|
+
*/
|
|
127
|
+
readonly fileData?: Maybe<string>;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Expands attached file references into file content parts.
|
|
131
|
+
*
|
|
132
|
+
* @param files - The attached file references.
|
|
133
|
+
* @returns One content part per file.
|
|
134
|
+
*/
|
|
135
|
+
export declare function openRouterInputFilePartsForAttachedFiles(files: Maybe<OpenRouterAttachedFileReference[]>): OpenRouterInputFilePart[];
|
|
136
|
+
/**
|
|
137
|
+
* Rewrites the `input_file` parts of an already-assembled conversation with the attachment resolved for
|
|
138
|
+
* THIS attempt, matching on filename.
|
|
139
|
+
*
|
|
140
|
+
* A conversation persisted mid-run carries whatever the attempt that persisted it was carrying —
|
|
141
|
+
* a url that has since expired, or (with inline attachments stripped on save) nothing at all. Replaying
|
|
142
|
+
* it unchanged is the failure mode most likely to reach production unnoticed, because it only shows up
|
|
143
|
+
* on a retry or a deferred resume. Resolving fresh per attempt is only half the fix; the other half is
|
|
144
|
+
* making sure the stored history is re-pointed at the fresh attachment too.
|
|
145
|
+
*
|
|
146
|
+
* The field the attachment does NOT carry is REMOVED rather than left alone: a stored `fileUrl` sitting
|
|
147
|
+
* next to a fresh `fileData` would send OpenRouter both, and it is not defined which one wins. Removed
|
|
148
|
+
* rather than nulled, because this output goes on the wire — the SDK validates `input_file` against a
|
|
149
|
+
* schema where an explicit `null` is not a legal absent value.
|
|
150
|
+
*
|
|
151
|
+
* A part whose filename matches nothing in `files` is left alone: it came from somewhere other than
|
|
152
|
+
* this task's file list, and guessing at it would be worse than leaving it.
|
|
153
|
+
*
|
|
154
|
+
* @param messages - The assembled conversation.
|
|
155
|
+
* @param files - The files attached for this attempt.
|
|
156
|
+
* @returns The conversation with fresh attachments, or the input unchanged when there is nothing to rewrite.
|
|
157
|
+
*/
|
|
158
|
+
export declare function openRouterMessagesWithFreshFileAttachments<T extends {
|
|
159
|
+
readonly role: string;
|
|
160
|
+
readonly content: unknown;
|
|
161
|
+
}>(messages: Maybe<T[]>, files: Maybe<OpenRouterAttachedFileReference[]>): T[];
|
|
162
|
+
/**
|
|
163
|
+
* Strips the attachment payload off every `input_file` part, keeping `filename` as the rejoin key.
|
|
164
|
+
*
|
|
165
|
+
* Applied on the way INTO Firestore. An attachment is resolved per attempt and is meaningless the
|
|
166
|
+
* moment that attempt ends — a signed url has expired, and inline base64 is the whole file, on a
|
|
167
|
+
* document with a 1 MiB ceiling and a `msg` field that already grows without bound. Nothing is lost:
|
|
168
|
+
* {@link openRouterMessagesWithFreshFileAttachments} re-points the stored parts on the way back out.
|
|
169
|
+
*
|
|
170
|
+
* @param messages - The conversation about to be persisted.
|
|
171
|
+
* @returns The conversation with attachment payloads removed.
|
|
172
|
+
*/
|
|
173
|
+
export declare function openRouterMessagesWithoutFileAttachmentData<T extends {
|
|
174
|
+
readonly role: string;
|
|
175
|
+
readonly content: unknown;
|
|
176
|
+
}>(messages: Maybe<T[]>): T[];
|
|
177
|
+
/**
|
|
178
|
+
* An assistant message echoing `file-parser` annotations back to OpenRouter.
|
|
179
|
+
*
|
|
180
|
+
* `annotations` is not part of the modeled input-message shape, so this is its own type. It is carried
|
|
181
|
+
* through the request builder as-is.
|
|
182
|
+
*/
|
|
183
|
+
export interface OpenRouterFileAnnotationEchoMessage {
|
|
184
|
+
readonly role: 'assistant';
|
|
185
|
+
readonly content: string;
|
|
186
|
+
readonly annotations: readonly {
|
|
187
|
+
readonly type: 'file';
|
|
188
|
+
readonly file: {
|
|
189
|
+
readonly hash: string;
|
|
190
|
+
readonly name?: Maybe<string>;
|
|
191
|
+
readonly content?: Maybe<unknown>;
|
|
192
|
+
};
|
|
193
|
+
}[];
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Renders one cached annotation as message text.
|
|
197
|
+
*
|
|
198
|
+
* @param annotation - The cached annotation.
|
|
199
|
+
* @returns The text carrying the parse.
|
|
200
|
+
*
|
|
201
|
+
* @__NO_SIDE_EFFECTS__
|
|
202
|
+
*/
|
|
203
|
+
export declare function openRouterFileAnnotationText(annotation: OpenRouterFileAnnotation): string;
|
|
204
|
+
/**
|
|
205
|
+
* Builds the assistant message that carries cached `file-parser` output back, so an already-parsed file
|
|
206
|
+
* is not parsed again.
|
|
207
|
+
*
|
|
208
|
+
* It carries the parse TWICE, deliberately, and for an empirical reason rather than a defensive one:
|
|
209
|
+
*
|
|
210
|
+
* - `annotations` is OpenRouter's own documented echo format, so the shape is kept and the mechanism
|
|
211
|
+
* starts working the day the Responses API models it. Today it does not: the SDK validates the request
|
|
212
|
+
* body against a closed union whose message variants have no `annotations` field, so the property is
|
|
213
|
+
* STRIPPED during outbound serialization. Verified against the wire, not assumed.
|
|
214
|
+
* - The parse is therefore ALSO rendered into `content`, as ordinary text, which does survive. Paired
|
|
215
|
+
* with the request builder dropping the file part for an already-parsed file, that is what makes the
|
|
216
|
+
* cache real today.
|
|
217
|
+
*
|
|
218
|
+
* @param annotations - The cached annotations.
|
|
219
|
+
* @returns The message, or undefined when there is nothing cached to resubmit.
|
|
220
|
+
*/
|
|
221
|
+
export declare function openRouterFileAnnotationMessage(annotations: Maybe<OpenRouterFileAnnotation[]>): Maybe<OpenRouterFileAnnotationEchoMessage>;
|
|
222
|
+
/**
|
|
223
|
+
* Drops the files whose parse is already cached.
|
|
224
|
+
*
|
|
225
|
+
* Not re-sending the document is what actually prevents a re-parse. The annotation echo alone cannot:
|
|
226
|
+
* the SDK strips it, and even where it survives it is a hint the provider is free to ignore, so a run
|
|
227
|
+
* that relied on it would pay for the parse again with no error to show for it.
|
|
228
|
+
*
|
|
229
|
+
* A file is matched to its cached parse by filename, which is the only handle both sides share — the
|
|
230
|
+
* annotation's `hash` is assigned by OpenRouter and the reference's `storagePath` is ours.
|
|
231
|
+
*
|
|
232
|
+
* @param files - The files attached for this attempt.
|
|
233
|
+
* @param annotations - The cached annotations.
|
|
234
|
+
* @returns The files that still need sending.
|
|
235
|
+
*
|
|
236
|
+
* @__NO_SIDE_EFFECTS__
|
|
237
|
+
*/
|
|
238
|
+
export declare function openRouterUnparsedAttachedFiles(files: Maybe<OpenRouterAttachedFileReference[]>, annotations: Maybe<OpenRouterFileAnnotation[]>): OpenRouterAttachedFileReference[];
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { type Maybe } from '@dereekb/util';
|
|
2
|
+
import { type OpenRouterModelConfig } from './openrouter.config';
|
|
3
|
+
import { type OpenRouterInputRole } from './openrouter.input';
|
|
4
|
+
import { type OpenRouterPromptKey, type OpenRouterPromptVersionNumber } from './openrouter.type';
|
|
5
|
+
/**
|
|
6
|
+
* A seed message stored on a prompt version.
|
|
7
|
+
*
|
|
8
|
+
* These are the STATIC half of a prompt — the part that is identical on every call. They are emitted
|
|
9
|
+
* before the caller's dynamic input so a prompt-cache prefix hit stays possible.
|
|
10
|
+
*/
|
|
11
|
+
export interface OpenRouterPromptSeedMessage {
|
|
12
|
+
readonly role: OpenRouterInputRole;
|
|
13
|
+
readonly content: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A prompt version resolved to everything needed to build a request.
|
|
17
|
+
*
|
|
18
|
+
* This is the replacement for an OpenAI `pmpt_…` id: instead of a dashboard-hosted object referenced
|
|
19
|
+
* by an opaque id, the content, the model, and the output format are values we hold.
|
|
20
|
+
*
|
|
21
|
+
* There is deliberately NO variables model. Dynamic content is passed as `input` from typed
|
|
22
|
+
* TypeScript — which is what OpenRouter requires (no server-side substitution) and what OpenAI's own
|
|
23
|
+
* migration guide prescribes: "Replace prompt variables with function arguments."
|
|
24
|
+
*/
|
|
25
|
+
export interface OpenRouterResolvedPrompt {
|
|
26
|
+
/**
|
|
27
|
+
* The prompt this was resolved from.
|
|
28
|
+
*/
|
|
29
|
+
readonly promptKey: OpenRouterPromptKey;
|
|
30
|
+
/**
|
|
31
|
+
* The version that was resolved — either the caller's pin, or the prompt's active version.
|
|
32
|
+
*
|
|
33
|
+
* Recorded on every run so a result can always be traced back to the exact prompt text that
|
|
34
|
+
* produced it, and so a historical run can be replayed against it.
|
|
35
|
+
*/
|
|
36
|
+
readonly version: OpenRouterPromptVersionNumber;
|
|
37
|
+
/**
|
|
38
|
+
* System prompt.
|
|
39
|
+
*/
|
|
40
|
+
readonly instructions?: Maybe<string>;
|
|
41
|
+
/**
|
|
42
|
+
* Static seed messages.
|
|
43
|
+
*/
|
|
44
|
+
readonly messages?: Maybe<OpenRouterPromptSeedMessage[]>;
|
|
45
|
+
/**
|
|
46
|
+
* The version's model config.
|
|
47
|
+
*/
|
|
48
|
+
readonly config: OpenRouterModelConfig;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A prompt defined in CODE rather than published to Firestore.
|
|
52
|
+
*
|
|
53
|
+
* This is the backup half of prompt resolution: an app ships its prompts as definitions, so a fresh
|
|
54
|
+
* environment — a new emulator, a test, a project that has never been seeded — can serve them without a
|
|
55
|
+
* manual seeding step first. Once the same prompt is published to Firestore the stored version takes
|
|
56
|
+
* over, which is what keeps prompt authoring a runtime concern rather than a deploy-shaped one.
|
|
57
|
+
*
|
|
58
|
+
* A definition IS an {@link OpenRouterResolvedPrompt}, so serving one needs no conversion. The extra
|
|
59
|
+
* fields are the authoring metadata a seeder needs to create the stored prompt from this same value,
|
|
60
|
+
* rather than restating the name and description alongside it.
|
|
61
|
+
*/
|
|
62
|
+
export interface OpenRouterPromptDefinition extends OpenRouterResolvedPrompt {
|
|
63
|
+
/**
|
|
64
|
+
* The version this code ships.
|
|
65
|
+
*
|
|
66
|
+
* Compared against the stored prompt's active version to decide which one wins, so this is the one
|
|
67
|
+
* knob that controls drift: bump it when the definition's text or config changes and the definition
|
|
68
|
+
* takes over again, even from an environment that was already seeded at a lower version.
|
|
69
|
+
*/
|
|
70
|
+
readonly version: OpenRouterPromptVersionNumber;
|
|
71
|
+
/**
|
|
72
|
+
* Human-readable name, used when this definition is published to Firestore.
|
|
73
|
+
*/
|
|
74
|
+
readonly name: string;
|
|
75
|
+
/**
|
|
76
|
+
* What this prompt is for, used when this definition is published to Firestore.
|
|
77
|
+
*/
|
|
78
|
+
readonly description?: Maybe<string>;
|
|
79
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { type Maybe } from '@dereekb/util';
|
|
2
|
+
import { type OpenRouterModelConfig } from './openrouter.config';
|
|
3
|
+
import { type OpenRouterAttachedFileReference, type OpenRouterFileAnnotationEchoMessage, type OpenRouterInput, type OpenRouterInputMessage, openRouterFileAnnotationMessage } from './openrouter.input';
|
|
4
|
+
import { type OpenRouterResolvedPrompt } from './openrouter.prompt';
|
|
5
|
+
import { type OpenRouterRunTaskKey } from './openrouter.type';
|
|
6
|
+
/**
|
|
7
|
+
* Trace metadata attached to a request.
|
|
8
|
+
*
|
|
9
|
+
* OpenRouter includes custom metadata from the `trace` field as span attributes in the OTLP payload
|
|
10
|
+
* its broadcast webhook posts, so this is the correlation handle that lets a completion trace be
|
|
11
|
+
* matched back to the run that produced it — the same job `metadata.taskKey` does on OpenAI today.
|
|
12
|
+
*/
|
|
13
|
+
export interface OpenRouterRequestTrace {
|
|
14
|
+
/**
|
|
15
|
+
* The run task this request belongs to.
|
|
16
|
+
*/
|
|
17
|
+
readonly runTaskKey?: Maybe<OpenRouterRunTaskKey>;
|
|
18
|
+
/**
|
|
19
|
+
* Any additional properties to carry through as span attributes.
|
|
20
|
+
*/
|
|
21
|
+
readonly [key: string]: unknown;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Params for {@link openRouterPromptRequest}.
|
|
25
|
+
*/
|
|
26
|
+
export interface OpenRouterPromptRequestParams {
|
|
27
|
+
/**
|
|
28
|
+
* The resolved prompt version supplying instructions, seed messages, and base config.
|
|
29
|
+
*/
|
|
30
|
+
readonly prompt: OpenRouterResolvedPrompt;
|
|
31
|
+
/**
|
|
32
|
+
* The caller's dynamic input.
|
|
33
|
+
*/
|
|
34
|
+
readonly input?: Maybe<OpenRouterInput>;
|
|
35
|
+
/**
|
|
36
|
+
* Per-run config overrides, applied on top of the version's config.
|
|
37
|
+
*/
|
|
38
|
+
readonly overrides?: Maybe<OpenRouterModelConfig>;
|
|
39
|
+
/**
|
|
40
|
+
* Files to attach, each already resolved to a url or inline data for THIS attempt.
|
|
41
|
+
*/
|
|
42
|
+
readonly files?: Maybe<OpenRouterAttachedFileReference[]>;
|
|
43
|
+
/**
|
|
44
|
+
* Cached `file-parser` annotations to echo back so an already-parsed file is not re-parsed.
|
|
45
|
+
*/
|
|
46
|
+
readonly fileAnnotations?: Maybe<Parameters<typeof openRouterFileAnnotationMessage>[0]>;
|
|
47
|
+
/**
|
|
48
|
+
* Prior conversation history to continue from.
|
|
49
|
+
*
|
|
50
|
+
* This is what replaces `previous_response_id`: OpenRouter is stateless, so continuing a
|
|
51
|
+
* conversation means resending its history.
|
|
52
|
+
*/
|
|
53
|
+
readonly history?: Maybe<OpenRouterInputMessage[]>;
|
|
54
|
+
/**
|
|
55
|
+
* Trace metadata for cost/usage reconciliation.
|
|
56
|
+
*/
|
|
57
|
+
readonly trace?: Maybe<OpenRouterRequestTrace>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* A built request: the merged config plus the assembled input, ready to spread into `callModel`.
|
|
61
|
+
*/
|
|
62
|
+
export interface OpenRouterPromptRequest {
|
|
63
|
+
/**
|
|
64
|
+
* The merged model config (version config with overrides applied).
|
|
65
|
+
*/
|
|
66
|
+
readonly config: OpenRouterModelConfig;
|
|
67
|
+
/**
|
|
68
|
+
* The system prompt.
|
|
69
|
+
*/
|
|
70
|
+
readonly instructions?: Maybe<string>;
|
|
71
|
+
/**
|
|
72
|
+
* The assembled input messages, static content first.
|
|
73
|
+
*/
|
|
74
|
+
readonly input: (OpenRouterInputMessage | OpenRouterFileAnnotationEchoMessage)[];
|
|
75
|
+
/**
|
|
76
|
+
* Trace metadata, when any was supplied.
|
|
77
|
+
*/
|
|
78
|
+
readonly trace?: Maybe<OpenRouterRequestTrace>;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Builds a request from a resolved prompt version plus a caller's dynamic input.
|
|
82
|
+
*
|
|
83
|
+
* Content is emitted STATIC FIRST — seed messages, then annotation echoes, then continued history,
|
|
84
|
+
* then the caller's input, with attached files appended to the final user message. That ordering is
|
|
85
|
+
* not cosmetic: a prompt cache only hits on a shared prefix, so putting the per-call content last is
|
|
86
|
+
* what keeps the static prefix cacheable across runs of the same prompt.
|
|
87
|
+
*
|
|
88
|
+
* @param params - The prompt, input, overrides, files, history, and trace.
|
|
89
|
+
* @returns The built request.
|
|
90
|
+
*/
|
|
91
|
+
export declare function openRouterPromptRequest(params: OpenRouterPromptRequestParams): OpenRouterPromptRequest;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single boundary between this package and `@openrouter/sdk`.
|
|
3
|
+
*
|
|
4
|
+
* The SDK publishes its agent surface (`callModel`, tools, conversation state, stop conditions) from
|
|
5
|
+
* deep subpaths rather than its root, so those import specifiers are inherently brittle. They are
|
|
6
|
+
* confined to this one file: an SDK reorganisation is then a change here rather than across the
|
|
7
|
+
* package.
|
|
8
|
+
*
|
|
9
|
+
* `@openrouter/agent` is deliberately NOT used. It declares `"@openrouter/sdk": "^0.13.7"`, so npm nests a
|
|
10
|
+
* second copy of the SDK — and the two `OpenRouterCore` classes are distinct nominal types (different
|
|
11
|
+
* `#private` brands), so a client built from the 1.2.x SDK cannot be passed to its `callModel` at all. The
|
|
12
|
+
* only thing it adds is a first-class deferred-tool API, and the 1.2.x SDK's manual tools
|
|
13
|
+
* (`execute: false`) plus `ConversationState.pendingToolCalls` / `unsentToolResults` already provide that
|
|
14
|
+
* mechanism. See `openrouter.tool.ts`.
|
|
15
|
+
*
|
|
16
|
+
* `responsesSend` / `ModelResult` / `convertToolsToAPIFormat` are what make hosted (server-executed) tools
|
|
17
|
+
* deliverable: `callModel` owns the `tools` key and converts every entry as a client function tool, so
|
|
18
|
+
* taking its transport and its tool loop directly is what lets `openrouter.call.ts` put an
|
|
19
|
+
* already-converted tool array on the request. See `openRouterModelResultForRequest`.
|
|
20
|
+
*/
|
|
21
|
+
export { callModel } from '@openrouter/sdk/funcs/call-model';
|
|
22
|
+
export { responsesSend } from '@openrouter/sdk/funcs/responsesSend';
|
|
23
|
+
export { unsentResultsToAPIFormat } from '@openrouter/sdk/lib/conversation-state';
|
|
24
|
+
export { embeddingsGenerate } from '@openrouter/sdk/funcs/embeddingsGenerate';
|
|
25
|
+
export { generationsGetGeneration } from '@openrouter/sdk/funcs/generationsGetGeneration';
|
|
26
|
+
export { generationsListGenerationContent } from '@openrouter/sdk/funcs/generationsListGenerationContent';
|
|
27
|
+
export { ModelResult } from '@openrouter/sdk/lib/model-result';
|
|
28
|
+
export { tool } from '@openrouter/sdk/lib/tool';
|
|
29
|
+
export { convertToolsToAPIFormat } from '@openrouter/sdk/lib/tool-executor';
|
|
30
|
+
export { finishReasonIs, hasToolCall, maxCost, maxTokensUsed, stepCountIs } from '@openrouter/sdk/lib/stop-conditions';
|
|
31
|
+
export { isManualTool, ToolType } from '@openrouter/sdk/lib/tool-types';
|
|
32
|
+
export type { OpenRouterCore } from '@openrouter/sdk/core';
|
|
33
|
+
export type { CallModelInput } from '@openrouter/sdk/lib/async-params';
|
|
34
|
+
export type { RequestOptions } from '@openrouter/sdk/lib/sdks';
|
|
35
|
+
export type { ConversationState, ConversationStatus, ParsedToolCall, StateAccessor, StopWhen, Tool, UnsentToolResult } from '@openrouter/sdk/lib/tool-types';
|
|
36
|
+
export type { FunctionCallOutputItem, GenerationContentData, GenerationResponseData, InputsUnion, OpenResponsesResult, ResponsesRequest, Usage } from '@openrouter/sdk/models';
|
|
37
|
+
export type { CreateEmbeddingsRequest, CreateEmbeddingsResponseBody, CreateResponsesResponse } from '@openrouter/sdk/models/operations';
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { type Maybe } from '@dereekb/util';
|
|
2
|
+
import { type ConversationState, type FunctionCallOutputItem, type ParsedToolCall, type Tool, type UnsentToolResult } from './openrouter.sdk';
|
|
3
|
+
import { type OpenRouterDeferredToolTaskId } from './openrouter.type';
|
|
4
|
+
/**
|
|
5
|
+
* The tool-call id the SDK assigns to one invocation.
|
|
6
|
+
*/
|
|
7
|
+
export type OpenRouterToolCallId = string;
|
|
8
|
+
/**
|
|
9
|
+
* A tool call that is waiting on a result from outside this process.
|
|
10
|
+
*
|
|
11
|
+
* A deferred tool is a MANUAL tool (`execute: false`): the SDK emits the call, does not run it, and
|
|
12
|
+
* pauses. Some other system — a human, a queue worker, an inbound webhook — produces the result and
|
|
13
|
+
* hands it back via {@link openRouterResolvedDeferredToolResults}, at which point the run continues.
|
|
14
|
+
*
|
|
15
|
+
* `taskId` is ours, not OpenRouter's. Nothing upstream allocates it, so a caller that wants to
|
|
16
|
+
* correlate a pause with a ticket in its own system simply uses that ticket's id.
|
|
17
|
+
*/
|
|
18
|
+
export interface OpenRouterPendingDeferredToolCall {
|
|
19
|
+
readonly callId: OpenRouterToolCallId;
|
|
20
|
+
readonly name: string;
|
|
21
|
+
/**
|
|
22
|
+
* The task id the resolving system will quote. Defaults to `callId` when the caller supplies none.
|
|
23
|
+
*/
|
|
24
|
+
readonly taskId: OpenRouterDeferredToolTaskId;
|
|
25
|
+
/**
|
|
26
|
+
* The arguments the model called the tool with.
|
|
27
|
+
*/
|
|
28
|
+
readonly arguments?: Maybe<Record<string, unknown>>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A resolution for one pending deferred tool call: either a successful output or an error.
|
|
32
|
+
*/
|
|
33
|
+
export type OpenRouterDeferredToolResolution = {
|
|
34
|
+
readonly taskId: OpenRouterDeferredToolTaskId;
|
|
35
|
+
readonly output: unknown;
|
|
36
|
+
readonly error?: never;
|
|
37
|
+
} | {
|
|
38
|
+
readonly taskId: OpenRouterDeferredToolTaskId;
|
|
39
|
+
readonly error: string;
|
|
40
|
+
readonly output?: never;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Extracts the deferred (manual) tool calls from a conversation state.
|
|
44
|
+
*
|
|
45
|
+
* Only manual tools can be pending on resume: a tool with an `execute` function was run in-process
|
|
46
|
+
* before the state was ever saved.
|
|
47
|
+
*
|
|
48
|
+
* @param state - The conversation state.
|
|
49
|
+
* @param tools - The tool set the run was configured with, used to tell manual tools from executable ones.
|
|
50
|
+
* @returns The pending deferred tool calls.
|
|
51
|
+
*/
|
|
52
|
+
export declare function openRouterPendingDeferredToolCalls<TTools extends readonly Tool[] = readonly Tool[]>(state: Maybe<ConversationState<TTools>>, tools: Maybe<TTools>): OpenRouterPendingDeferredToolCall[];
|
|
53
|
+
/**
|
|
54
|
+
* Converts an SDK parsed tool call into a pending deferred tool call.
|
|
55
|
+
*
|
|
56
|
+
* @param call - The parsed tool call.
|
|
57
|
+
* @param taskId - Optional task id to associate. Defaults to the call id.
|
|
58
|
+
* @returns The pending deferred tool call.
|
|
59
|
+
*/
|
|
60
|
+
export declare function openRouterPendingDeferredToolCallFromParsedCall(call: ParsedToolCall<Tool>, taskId?: Maybe<OpenRouterDeferredToolTaskId>): OpenRouterPendingDeferredToolCall;
|
|
61
|
+
/**
|
|
62
|
+
* Turns resolutions into the `unsentToolResults` entries the SDK replays on the next call.
|
|
63
|
+
*
|
|
64
|
+
* A resolution whose `taskId` matches no pending call is DROPPED rather than throwing. Deferred
|
|
65
|
+
* resolutions arrive from outside this process and may be replayed — an unmatched one means the task
|
|
66
|
+
* was already settled, which must be a no-op, not a failure.
|
|
67
|
+
*
|
|
68
|
+
* @param pending - The currently pending deferred tool calls.
|
|
69
|
+
* @param resolutions - The resolutions received.
|
|
70
|
+
* @returns The unsent tool results, in the order the resolutions were given.
|
|
71
|
+
*/
|
|
72
|
+
export declare function openRouterResolvedDeferredToolResults<TTools extends readonly Tool[] = readonly Tool[]>(pending: Maybe<OpenRouterPendingDeferredToolCall[]>, resolutions: Maybe<OpenRouterDeferredToolResolution[]>): UnsentToolResult<TTools>[];
|
|
73
|
+
/**
|
|
74
|
+
* Whether a conversation state is paused waiting on a deferred tool result.
|
|
75
|
+
*
|
|
76
|
+
* @param state - The conversation state.
|
|
77
|
+
* @returns True when the state has pending tool calls it cannot resolve itself.
|
|
78
|
+
*/
|
|
79
|
+
export declare function isOpenRouterStateAwaitingDeferredTools(state: Maybe<ConversationState>): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Converts recorded tool results into the `function_call_output` items that get appended to the
|
|
82
|
+
* conversation before the run is resumed.
|
|
83
|
+
*
|
|
84
|
+
* This is how a deferred pause is un-paused, and it is done HERE rather than through the SDK on
|
|
85
|
+
* purpose. `@openrouter/sdk@1.2.x` only knows how to resume a pause by re-running the tool locally
|
|
86
|
+
* (`approveToolCalls` calls the tool's `execute`, and a manual tool has none) or by rejecting it — so
|
|
87
|
+
* a result produced by another process has no route back in through the SDK's own API. Appending the
|
|
88
|
+
* outputs to the persisted conversation and re-sending it does have one, and it is the same wire
|
|
89
|
+
* format the SDK would have produced itself.
|
|
90
|
+
*
|
|
91
|
+
* @param results - The recorded results, in the persisted `callId` / `name` / `output` / `error` shape.
|
|
92
|
+
* @returns The `function_call_output` items to append to the conversation.
|
|
93
|
+
*/
|
|
94
|
+
export declare function openRouterFunctionCallOutputItems(results: Maybe<readonly {
|
|
95
|
+
readonly callId: string;
|
|
96
|
+
readonly name: string;
|
|
97
|
+
readonly output?: unknown;
|
|
98
|
+
readonly error?: Maybe<string>;
|
|
99
|
+
}[]>): FunctionCallOutputItem[];
|