@dereekb/openrouter 14.7.0 → 14.9.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.
@@ -1,21 +1,21 @@
1
1
  {
2
2
  "name": "@dereekb/openrouter/firebase-server",
3
- "version": "14.7.0",
3
+ "version": "14.9.0",
4
4
  "sideEffects": false,
5
5
  "type": "module",
6
6
  "peerDependencies": {
7
- "@dereekb/analytics": "14.7.0",
8
- "@dereekb/date": "14.7.0",
9
- "@dereekb/firebase": "14.7.0",
10
- "@dereekb/firebase-server": "14.7.0",
11
- "@dereekb/model": "14.7.0",
12
- "@dereekb/nestjs": "14.7.0",
13
- "@dereekb/openrouter": "14.7.0",
14
- "@dereekb/rxjs": "14.7.0",
15
- "@dereekb/util": "14.7.0",
7
+ "@dereekb/analytics": "14.9.0",
8
+ "@dereekb/date": "14.9.0",
9
+ "@dereekb/firebase": "14.9.0",
10
+ "@dereekb/firebase-server": "14.9.0",
11
+ "@dereekb/model": "14.9.0",
12
+ "@dereekb/nestjs": "14.9.0",
13
+ "@dereekb/openrouter": "14.9.0",
14
+ "@dereekb/rxjs": "14.9.0",
15
+ "@dereekb/util": "14.9.0",
16
16
  "@nestjs/common": "^12.0.1",
17
17
  "@nestjs/config": "^12.0.0",
18
- "@openrouter/sdk": "^1.3.0",
18
+ "@openrouter/sdk": "^1.3.8",
19
19
  "arktype": "^2.2.0",
20
20
  "date-fns": "^4.1.0",
21
21
  "express": "^5.2.1",
@@ -23,7 +23,7 @@
23
23
  "make-error": "^1.3.6"
24
24
  },
25
25
  "devDependencies": {
26
- "@dereekb/nestjs": "14.7.0",
26
+ "@dereekb/nestjs": "14.9.0",
27
27
  "@nestjs/testing": "^12.0.1"
28
28
  },
29
29
  "exports": {
@@ -1,5 +1,5 @@
1
1
  import { type Maybe } from '@dereekb/util';
2
- import { type OpenRouterAttachedFileReference, type OpenRouterCallResult, type OpenRouterCore, type OpenRouterInput, type OpenRouterModelConfig, type OpenRouterPromptKey, type OpenRouterPromptVersionNumber, type Tool } from '@dereekb/openrouter';
2
+ import { type OpenRouterAttachedFileReference, type OpenRouterCallResult, type OpenRouterCore, type OpenRouterDecisionQuestions, type OpenRouterDecisionResult, type OpenRouterDecisionState, type OpenRouterInput, type OpenRouterModelConfig, type OpenRouterPromptKey, type OpenRouterPromptVersionNumber, type Tool } from '@dereekb/openrouter';
3
3
  import { type OpenRouterPromptService } from './openrouter.prompt.service';
4
4
  /**
5
5
  * Params for {@link callModelForPrompt}.
@@ -55,3 +55,60 @@ export interface CallModelForPromptParams {
55
55
  * @returns The normalized call result.
56
56
  */
57
57
  export declare function callModelForPrompt(params: CallModelForPromptParams): Promise<OpenRouterCallResult>;
58
+ /**
59
+ * Params for {@link decideForPrompt}.
60
+ */
61
+ export interface DecideForPromptParams {
62
+ /**
63
+ * The OpenRouter client.
64
+ */
65
+ readonly client: OpenRouterCore;
66
+ /**
67
+ * The prompt service used to resolve the version.
68
+ */
69
+ readonly promptService: OpenRouterPromptService;
70
+ /**
71
+ * The decision prompt to ask.
72
+ */
73
+ readonly promptKey: OpenRouterPromptKey;
74
+ /**
75
+ * Version to pin. Omit to use the prompt's active version.
76
+ */
77
+ readonly version?: Maybe<OpenRouterPromptVersionNumber>;
78
+ /**
79
+ * The content to judge.
80
+ */
81
+ readonly state: OpenRouterDecisionState;
82
+ /**
83
+ * Questions declared for THIS call, merged over the version's stored questions by id.
84
+ */
85
+ readonly questions?: Maybe<OpenRouterDecisionQuestions>;
86
+ /**
87
+ * Per-call config overrides.
88
+ */
89
+ readonly configOverrides?: Maybe<OpenRouterModelConfig>;
90
+ /**
91
+ * Observability grouping id.
92
+ */
93
+ readonly sessionId?: Maybe<string>;
94
+ /**
95
+ * Trace metadata for cost/usage reconciliation.
96
+ */
97
+ readonly trace?: Maybe<Record<string, unknown>>;
98
+ }
99
+ /**
100
+ * Asks a decision prompt INLINE and returns its answers, with no run task document.
101
+ *
102
+ * The default way to ask a decision. A System One call typically answers in around a hundred
103
+ * milliseconds, has no tools to loop and nothing to defer, so the queue's lease, sweep interval and
104
+ * retry ladder buy nothing on the happy path — paying for a document to run a call that finishes before
105
+ * the write does is pure overhead.
106
+ *
107
+ * Reach for `enqueueRunTask({ state, questions, immediate: true })` instead when a FAILURE needs to
108
+ * survive this process: that keeps the same run-it-now latency and leaves a retryable failure queued for
109
+ * the sweep, at the cost of one document per run.
110
+ *
111
+ * @param params - The client, prompt service, prompt, state, and questions.
112
+ * @returns The decision result.
113
+ */
114
+ export declare function decideForPrompt(params: DecideForPromptParams): Promise<OpenRouterDecisionResult>;
@@ -1,5 +1,31 @@
1
1
  import { type Maybe } from '@dereekb/util';
2
+ import { type OpenRouterDecisionAnswers, type OpenRouterDecisionQuestions } from '@dereekb/openrouter';
2
3
  import { type OpenRouterRunTask } from '@dereekb/openrouter/firebase';
4
+ /**
5
+ * Whether a run task is a DECISION run.
6
+ *
7
+ * Reads the state field, which is the discriminator — see `OpenRouterRunTask.st`.
8
+ *
9
+ * @param task - The run task.
10
+ * @returns True when the task asks a decision rather than a completion.
11
+ *
12
+ * @__NO_SIDE_EFFECTS__
13
+ */
14
+ export declare function isOpenRouterDecisionRunTask(task: Maybe<OpenRouterRunTask>): boolean;
15
+ /**
16
+ * Reads the answers off a completed decision run.
17
+ *
18
+ * The type parameter is the caller's own question map, which is what lets a consumer read
19
+ * `answers.urgency.noul` rather than walking an untyped record. It is an ASSERTION, not a check: the
20
+ * answers were membership-checked against the declared questions when they were written, so the only
21
+ * way this lies is a caller naming a different question map than the run used.
22
+ *
23
+ * @param task - The run task.
24
+ * @returns The answers, or undefined when the run is not a completed decision.
25
+ *
26
+ * @__NO_SIDE_EFFECTS__
27
+ */
28
+ export declare function openRouterRunTaskDecisionAnswers<Q extends OpenRouterDecisionQuestions = OpenRouterDecisionQuestions>(task: Maybe<OpenRouterRunTask>): Maybe<OpenRouterDecisionAnswers<Q>>;
3
29
  /**
4
30
  * How a caller should proceed given a run task's current state.
5
31
  *
@@ -1,7 +1,7 @@
1
1
  import { type FirebaseStorageContext } from '@dereekb/firebase';
2
2
  import { type FirebaseServerEnvService } from '@dereekb/firebase-server';
3
3
  import { type Maybe, type Milliseconds } from '@dereekb/util';
4
- import { type OpenRouterAttachedFileReference, type OpenRouterCallResult, type OpenRouterCore, type OpenRouterDeferredToolResolution, type OpenRouterFileReference, type OpenRouterInput, type OpenRouterModelConfig, type OpenRouterPromptKey, type OpenRouterPromptVersionNumber, type OpenRouterRunTaskKey, type Tool } from '@dereekb/openrouter';
4
+ import { type OpenRouterAttachedFileReference, type OpenRouterCallResult, type OpenRouterCore, type OpenRouterDecisionQuestions, type OpenRouterDecisionResult, type OpenRouterDeferredToolResolution, type OpenRouterFileReference, type OpenRouterInput, type OpenRouterModelConfig, type OpenRouterPromptKey, type OpenRouterPromptVersionNumber, type OpenRouterRunTaskKey, type OpenRouterStorableDecisionState, type Tool } from '@dereekb/openrouter';
5
5
  import { type OpenRouterRunTask, type OpenRouterRunTaskDocument, type OpenRouterRunTaskFirestoreCollections, type OpenRouterRunTaskPendingToolCall, OpenRouterRunTaskState, type OpenRouterRunTaskUnsentToolResult } from '@dereekb/openrouter/firebase';
6
6
  import { type OpenRouterFileAttachmentMode } from './openrouter.file.attachment';
7
7
  import { type OpenRouterPromptService } from './openrouter.prompt.service';
@@ -69,6 +69,30 @@ export interface OpenRouterEnqueueRunTaskParams {
69
69
  * that is already in flight or already finished.
70
70
  */
71
71
  readonly restart?: Maybe<boolean>;
72
+ /**
73
+ * The content to judge, making this a DECISION run.
74
+ *
75
+ * Presence of this is what sends the run to `POST /systemone` instead of `/responses`, and it is the
76
+ * only discriminator: which surface a request needs is a property of the request, and a second field
77
+ * declaring it is a second thing that can disagree.
78
+ */
79
+ readonly state?: Maybe<OpenRouterStorableDecisionState>;
80
+ /**
81
+ * Questions declared for THIS run, merged over the prompt version's own stored questions.
82
+ */
83
+ readonly questions?: Maybe<OpenRouterDecisionQuestions>;
84
+ /**
85
+ * Whether to run the task immediately rather than leaving it for the next sweep.
86
+ *
87
+ * The document is written either way, so a consumer's `readRunTask(key)` contract does not change
88
+ * shape depending on whether the inline attempt happened to succeed — which is the whole reason this
89
+ * is a flag on the queue rather than a separate inline call. What it buys is the queue's durability
90
+ * without the queue's latency: the common case answers within this call, and a run that fails
91
+ * RETRYABLY (OpenRouter down, a 429, a dropped connection) is left QUEUED for the sweep to pick up. A
92
+ * deterministic failure still reaches FAILED on the first attempt, because three sweep ticks would
93
+ * only reach the same answer more slowly.
94
+ */
95
+ readonly immediate?: Maybe<boolean>;
72
96
  }
73
97
  /**
74
98
  * Result of enqueueing a run task.
@@ -81,6 +105,14 @@ export interface OpenRouterEnqueueRunTaskResult {
81
105
  */
82
106
  readonly created: boolean;
83
107
  readonly task: OpenRouterRunTask;
108
+ /**
109
+ * What the immediate attempt did, when one was asked for and actually ran.
110
+ *
111
+ * Absent when `immediate` was not set, and also when it was but the task was not claimable — an
112
+ * already-running or already-finished run reached through an idempotent re-enqueue. A caller reads the
113
+ * document rather than assuming this is present.
114
+ */
115
+ readonly execution?: Maybe<OpenRouterRunTaskExecutionResult>;
84
116
  }
85
117
  /**
86
118
  * Params for claiming run tasks.
@@ -169,6 +201,10 @@ export interface OpenRouterRunTaskExecutionResult {
169
201
  readonly key: OpenRouterRunTaskKey;
170
202
  readonly state: OpenRouterRunTaskState;
171
203
  readonly result?: Maybe<OpenRouterCallResult>;
204
+ /**
205
+ * What a DECISION run produced. Present instead of `result`, never alongside it.
206
+ */
207
+ readonly decision?: Maybe<OpenRouterDecisionResult>;
172
208
  readonly error?: Maybe<unknown>;
173
209
  }
174
210
  /**
@@ -19,6 +19,20 @@ export interface FakeOpenRouterToolCall {
19
19
  readonly name: string;
20
20
  readonly arguments?: Maybe<Record<string, unknown>>;
21
21
  }
22
+ /**
23
+ * What the fake System One model should answer with, when the request went to `/systemone`.
24
+ */
25
+ export interface FakeOpenRouterDecisionReply {
26
+ /**
27
+ * The answers, keyed by question id, in the WIRE shape (`{ type, noul }`, `{ type, choice }`, …).
28
+ *
29
+ * Deliberately untyped: a fixture has to be able to produce replies the declaration types forbid,
30
+ * which is the only way the membership check gets exercised at all.
31
+ */
32
+ readonly answers?: Maybe<Record<string, unknown>>;
33
+ readonly inputTokens?: Maybe<number>;
34
+ readonly outputTokens?: Maybe<number>;
35
+ }
22
36
  /**
23
37
  * What the fake model should answer with on one turn.
24
38
  */
@@ -49,6 +63,14 @@ export interface FakeOpenRouterReply {
49
63
  * Thrown instead of answered, for the transport-failure path.
50
64
  */
51
65
  readonly throws?: Maybe<Error>;
66
+ /**
67
+ * An HTTP status to answer with instead of a success, for the retryable-vs-permanent classification.
68
+ */
69
+ readonly status?: Maybe<number>;
70
+ /**
71
+ * What to answer a DECISION request with. Ignored on a request that went to `/responses`.
72
+ */
73
+ readonly decision?: Maybe<FakeOpenRouterDecisionReply>;
52
74
  }
53
75
  /**
54
76
  * Decides the reply for one request. Receives the request body exactly as it went on the wire.
@@ -63,8 +85,31 @@ export interface FakeOpenRouterClient {
63
85
  * Every request body sent, in order, serialized exactly as OpenRouter would have received it.
64
86
  */
65
87
  readonly requests: Record<string, unknown>[];
88
+ /**
89
+ * Every request URL sent, in order, positionally matching {@link FakeOpenRouterClient.requests}.
90
+ *
91
+ * Recorded because the body alone cannot tell a completion from a decision, and because a fake that
92
+ * never looks at the URL stays green no matter which route the SDK resolves — which is how this
93
+ * vendor's decisions endpoint was once shipped 404ing on every live call with a fully green suite.
94
+ */
95
+ readonly urls: string[];
66
96
  readonly callCount: number;
67
97
  }
98
+ /**
99
+ * The path a System One (decisions) request goes to.
100
+ */
101
+ export declare const FAKE_OPENROUTER_SYSTEM_ONE_PATH = "/systemone";
102
+ /**
103
+ * Builds a `DecisionsResponse` JSON body in the WIRE shape the SDK's inbound schema parses.
104
+ *
105
+ * Note the snake_case token counts: the SDK decodes those to camelCase, so a fake emitting the decoded
106
+ * names would hide the rename the usage mapper reads through.
107
+ *
108
+ * @param reply - What the fake System One model should answer with.
109
+ * @param index - The call ordinal, used to make a distinct generation id.
110
+ * @returns The response body.
111
+ */
112
+ export declare function fakeOpenRouterDecisionResponseBody(reply: Maybe<FakeOpenRouterDecisionReply>, index: number): Record<string, unknown>;
68
113
  /**
69
114
  * Builds an `OpenResponsesResult` JSON body in the wire (snake_case) shape the SDK's inbound schema
70
115
  * parses.