@cat-factory/orchestration 0.283.2 → 0.284.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/dist/container/dependencies.d.ts +16 -1
- package/dist/container/dependencies.d.ts.map +1 -1
- package/dist/container/runtime.d.ts +1 -0
- package/dist/container/runtime.d.ts.map +1 -1
- package/dist/container/runtime.js +5 -1
- package/dist/container/runtime.js.map +1 -1
- package/dist/container/use-case-service.d.ts +20 -0
- package/dist/container/use-case-service.d.ts.map +1 -0
- package/dist/container/use-case-service.js +62 -0
- package/dist/container/use-case-service.js.map +1 -0
- package/dist/container.d.ts +16 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +11 -1
- package/dist/container.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/modules/useCases/InlineUseCaseService.d.ts +79 -0
- package/dist/modules/useCases/InlineUseCaseService.d.ts.map +1 -0
- package/dist/modules/useCases/InlineUseCaseService.js +220 -0
- package/dist/modules/useCases/InlineUseCaseService.js.map +1 -0
- package/dist/modules/useCases/LlmInlineUseCaseGenerator.d.ts +39 -0
- package/dist/modules/useCases/LlmInlineUseCaseGenerator.d.ts.map +1 -0
- package/dist/modules/useCases/LlmInlineUseCaseGenerator.js +224 -0
- package/dist/modules/useCases/LlmInlineUseCaseGenerator.js.map +1 -0
- package/dist/modules/useCases/useCaseUsage.d.ts +16 -0
- package/dist/modules/useCases/useCaseUsage.d.ts.map +1 -0
- package/dist/modules/useCases/useCaseUsage.js +40 -0
- package/dist/modules/useCases/useCaseUsage.js.map +1 -0
- package/dist/validation/validateInlineUseCases.d.ts +30 -0
- package/dist/validation/validateInlineUseCases.d.ts.map +1 -0
- package/dist/validation/validateInlineUseCases.js +114 -0
- package/dist/validation/validateInlineUseCases.js.map +1 -0
- package/dist/validation/validateRegistrations.d.ts +9 -1
- package/dist/validation/validateRegistrations.d.ts.map +1 -1
- package/dist/validation/validateRegistrations.js +23 -0
- package/dist/validation/validateRegistrations.js.map +1 -1
- package/package.json +11 -11
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { sanitizeDescriptorFields, validateDescriptorFields, withDescriptorFieldDefaults, } from '@cat-factory/contracts';
|
|
2
|
+
import { composeUseCasePrompt, describeError, NotFoundError, RateLimitedError, resolveUseCaseModelOption, UnavailableError, useCaseGenerationLimits, ValidationError, } from '@cat-factory/kernel';
|
|
3
|
+
/** The parameters a use case declares, as a list (absent ⇒ none). */
|
|
4
|
+
function parametersOf(useCase) {
|
|
5
|
+
return useCase.parameters ?? [];
|
|
6
|
+
}
|
|
7
|
+
export class InlineUseCaseService {
|
|
8
|
+
deps;
|
|
9
|
+
constructor(deps) {
|
|
10
|
+
this.deps = deps;
|
|
11
|
+
}
|
|
12
|
+
/** Every registered use case, projected for this scope (model availability included). */
|
|
13
|
+
async list(scope) {
|
|
14
|
+
const session = await this.discoverySession(scope);
|
|
15
|
+
return this.deps.registry.all().map((useCase) => this.project(useCase, session));
|
|
16
|
+
}
|
|
17
|
+
/** One registered use case by id, projected for this scope. */
|
|
18
|
+
async get(scope, useCaseId) {
|
|
19
|
+
const useCase = this.require(useCaseId);
|
|
20
|
+
return this.project(useCase, await this.discoverySession(scope));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Run one use case and answer with the generated text.
|
|
24
|
+
*
|
|
25
|
+
* The refusal ORDER is the point, and it is cheapest-first for the same reason the bug hunt's is:
|
|
26
|
+
* every step past the last one costs more than the step before it, so a request that was never
|
|
27
|
+
* going to run spends nothing. The registration lookup and the parameter check read nothing at
|
|
28
|
+
* all; binding the session reads the credential pool; the budget probe reads the spend ledger;
|
|
29
|
+
* only then does a vendor see a token.
|
|
30
|
+
*/
|
|
31
|
+
async invoke(input) {
|
|
32
|
+
const useCase = this.require(input.useCaseId);
|
|
33
|
+
const generator = this.requireGenerator();
|
|
34
|
+
const parameters = this.validateParameters(useCase, input.parameters ?? {});
|
|
35
|
+
const option = this.requireModelOption(useCase, input.model);
|
|
36
|
+
const limits = useCaseGenerationLimits(useCase);
|
|
37
|
+
const temperature = requireWithinBounds(input.temperature, limits.temperature, 'temperature');
|
|
38
|
+
const maxOutputTokens = requireWithinBounds(input.maxOutputTokens, limits.maxOutputTokens, 'maxOutputTokens');
|
|
39
|
+
// NOT caught here, unlike the discovery path: a credential pool that could not be read is not
|
|
40
|
+
// an availability answer, and reporting it as "this model is unavailable" would send the caller
|
|
41
|
+
// to pick another one for a fault that has nothing to do with the model they named.
|
|
42
|
+
const session = await generator.forScope(input.scope);
|
|
43
|
+
const availability = session.availability(option);
|
|
44
|
+
if (!availability.available) {
|
|
45
|
+
throw new UnavailableError(`The model '${option.label}' cannot be served by this deployment`, 'use_case_model_unavailable', { model: option.id, cause: availability.reason });
|
|
46
|
+
}
|
|
47
|
+
if (await this.deps.isOverBudget?.(input.scope)) {
|
|
48
|
+
// Its OWN refusal rather than a generic failure: an exhausted budget is not a broken model,
|
|
49
|
+
// and the fix (raise the budget, or wait for the window to roll) is not the fix for a
|
|
50
|
+
// misconfigured provider. Fail-CLOSED, so no vendor call is made.
|
|
51
|
+
throw new RateLimitedError('This workspace has spent its configured model budget', 'budget_exhausted');
|
|
52
|
+
}
|
|
53
|
+
const prompt = composeUseCasePrompt(useCase, {
|
|
54
|
+
useCaseId: useCase.useCaseId,
|
|
55
|
+
workspaceId: input.scope.workspaceId,
|
|
56
|
+
parameters,
|
|
57
|
+
fields: parametersOf(useCase),
|
|
58
|
+
});
|
|
59
|
+
const generation = await session.generate({
|
|
60
|
+
useCaseId: useCase.useCaseId,
|
|
61
|
+
option,
|
|
62
|
+
system: prompt.system,
|
|
63
|
+
prompt: prompt.prompt,
|
|
64
|
+
temperature,
|
|
65
|
+
maxOutputTokens,
|
|
66
|
+
});
|
|
67
|
+
if (generation.text.trim() === '') {
|
|
68
|
+
// An empty visible reply means the model answered only into its private reasoning channel
|
|
69
|
+
// (seen on some reasoning models) or refused without saying so. Either way there is nothing
|
|
70
|
+
// to return, and a 200 carrying an empty string would read to a content editor as a model
|
|
71
|
+
// that had nothing to say about the scene.
|
|
72
|
+
this.deps.logger?.warn('An inline use case produced no text', {
|
|
73
|
+
workspaceId: input.scope.workspaceId,
|
|
74
|
+
useCaseId: useCase.useCaseId,
|
|
75
|
+
model: option.id,
|
|
76
|
+
finishReason: generation.finishReason,
|
|
77
|
+
});
|
|
78
|
+
throw new UnavailableError(`The model '${option.label}' returned no usable text`, 'use_case_empty_reply', { model: option.id, finishReason: generation.finishReason });
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
useCaseId: useCase.useCaseId,
|
|
82
|
+
model: {
|
|
83
|
+
id: option.id,
|
|
84
|
+
label: option.label,
|
|
85
|
+
provider: generation.ref.provider,
|
|
86
|
+
model: generation.ref.model,
|
|
87
|
+
},
|
|
88
|
+
text: generation.text,
|
|
89
|
+
finishReason: generation.finishReason,
|
|
90
|
+
truncated: generation.finishReason === 'length',
|
|
91
|
+
usage: generation.usage,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/** The registered use case, or a 404 naming what was asked for. */
|
|
95
|
+
require(useCaseId) {
|
|
96
|
+
const useCase = this.deps.registry.get(useCaseId);
|
|
97
|
+
if (!useCase)
|
|
98
|
+
throw new NotFoundError('Use case', useCaseId, { reason: 'use_case_not_found' });
|
|
99
|
+
return useCase;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The generator, or a 503 naming the deployment-level gap.
|
|
103
|
+
*
|
|
104
|
+
* Distinct from a model being unavailable, which is what the per-option availability answers: an
|
|
105
|
+
* unconfigured deployment is fixed by wiring a model provider, and reporting every declared model
|
|
106
|
+
* as individually unavailable would send an operator looking for four missing keys instead.
|
|
107
|
+
*/
|
|
108
|
+
requireGenerator() {
|
|
109
|
+
const generator = this.deps.generator;
|
|
110
|
+
if (!generator?.enabled) {
|
|
111
|
+
throw new UnavailableError('No model provider is configured, so use cases cannot be invoked', 'use_case_models_unconfigured');
|
|
112
|
+
}
|
|
113
|
+
return generator;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The session a DISCOVERY read projects against, or `undefined` when there is none to be had.
|
|
117
|
+
*
|
|
118
|
+
* Two causes land on the same undefined, and each is stated where it happens rather than
|
|
119
|
+
* inferred here: no provider is wired at all (ordinary, and the catalog says so per option), or
|
|
120
|
+
* the credential pool could not be read (a real fault, logged with its cause). Discovery answers
|
|
121
|
+
* either way, because a read that 500s tells a wrapper the surface does not exist when what
|
|
122
|
+
* failed was one query behind it.
|
|
123
|
+
*/
|
|
124
|
+
async discoverySession(scope) {
|
|
125
|
+
const generator = this.deps.generator;
|
|
126
|
+
if (!generator?.enabled)
|
|
127
|
+
return undefined;
|
|
128
|
+
try {
|
|
129
|
+
return await generator.forScope(scope);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
this.deps.logger?.warn('Use-case model availability could not be resolved', {
|
|
133
|
+
workspaceId: scope.workspaceId,
|
|
134
|
+
...describeError(error),
|
|
135
|
+
});
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* The caller's bag, checked against the declared parameters and frozen.
|
|
141
|
+
*
|
|
142
|
+
* The SHARED descriptor validator, so this surface refuses exactly what a reusable operation's
|
|
143
|
+
* create door refuses: unknown keys, wrong value types, missing required visible fields, values
|
|
144
|
+
* outside a `select`'s options. Every problem is reported at once, because a caller filling a
|
|
145
|
+
* form wants the whole list rather than one field per round trip.
|
|
146
|
+
*/
|
|
147
|
+
validateParameters(useCase, supplied) {
|
|
148
|
+
const fields = parametersOf(useCase);
|
|
149
|
+
const withDefaults = withDescriptorFieldDefaults(fields, supplied);
|
|
150
|
+
const problems = validateDescriptorFields(fields, withDefaults);
|
|
151
|
+
if (problems.length > 0) {
|
|
152
|
+
throw new ValidationError(problems.join('; '), {
|
|
153
|
+
reason: 'use_case_parameters_invalid',
|
|
154
|
+
problems,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
return sanitizeDescriptorFields(fields, withDefaults);
|
|
158
|
+
}
|
|
159
|
+
/** The option the invocation runs on, or a 422 naming what this use case does carry. */
|
|
160
|
+
requireModelOption(useCase, requested) {
|
|
161
|
+
const option = resolveUseCaseModelOption(useCase, requested);
|
|
162
|
+
if (!option) {
|
|
163
|
+
throw new ValidationError(`The use case '${useCase.useCaseId}' does not offer a model '${requested ?? ''}'`, {
|
|
164
|
+
reason: 'use_case_model_not_allowed',
|
|
165
|
+
// The allowed ids, so a caller holding a stale catalog can correct itself from the
|
|
166
|
+
// refusal rather than re-reading discovery to find out what changed.
|
|
167
|
+
allowed: useCase.models.map((declared) => declared.id),
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return option;
|
|
171
|
+
}
|
|
172
|
+
/** One use case's wire projection, with each model's availability read off the bound session. */
|
|
173
|
+
project(useCase, session) {
|
|
174
|
+
return {
|
|
175
|
+
useCaseId: useCase.useCaseId,
|
|
176
|
+
label: useCase.label,
|
|
177
|
+
description: useCase.description,
|
|
178
|
+
...(useCase.category ? { category: useCase.category } : {}),
|
|
179
|
+
models: useCase.models.map((option) => projectModel(useCase, option, session)),
|
|
180
|
+
parameters: [...parametersOf(useCase)],
|
|
181
|
+
generation: useCaseGenerationLimits(useCase),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/** One model option's wire projection. */
|
|
186
|
+
function projectModel(useCase, option, session) {
|
|
187
|
+
// With no session there is no provider to ask, so every option is unavailable for the same
|
|
188
|
+
// deployment-level cause. Reported per option rather than omitted: a wrapper rendering the
|
|
189
|
+
// picker still shows what this use case OFFERS, greyed out, instead of an empty list that reads
|
|
190
|
+
// like a use case with no models declared.
|
|
191
|
+
const availability = session
|
|
192
|
+
? session.availability(option)
|
|
193
|
+
: { available: false, reason: 'provider_unavailable' };
|
|
194
|
+
return {
|
|
195
|
+
id: option.id,
|
|
196
|
+
label: option.label,
|
|
197
|
+
...(option.description ? { description: option.description } : {}),
|
|
198
|
+
default: resolveUseCaseModelOption(useCase, undefined)?.id === option.id,
|
|
199
|
+
available: availability.available,
|
|
200
|
+
...(availability.available ? {} : { unavailableReason: availability.reason }),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* A knob the caller named, checked against the declared bounds, else the declared default.
|
|
205
|
+
*
|
|
206
|
+
* REFUSED rather than clamped, which is why the name says `require` and not `clamp`. A caller
|
|
207
|
+
* asking for temperature 2.5 against a ceiling of 1.2 is asking for output the deployment has
|
|
208
|
+
* decided not to produce, and silently running at 1.2 answers that request with a different
|
|
209
|
+
* generation while reporting success: the caller stores the text believing it came from the
|
|
210
|
+
* settings it asked for.
|
|
211
|
+
*/
|
|
212
|
+
function requireWithinBounds(value, limit, name) {
|
|
213
|
+
if (value === undefined)
|
|
214
|
+
return limit.default;
|
|
215
|
+
if (!Number.isFinite(value) || value < limit.min || value > limit.max) {
|
|
216
|
+
throw new ValidationError(`'${name}' must be between ${limit.min} and ${limit.max} for this use case`, { reason: 'use_case_generation_out_of_range', field: name, min: limit.min, max: limit.max });
|
|
217
|
+
}
|
|
218
|
+
return value;
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=InlineUseCaseService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InlineUseCaseService.js","sourceRoot":"","sources":["../../../src/modules/useCases/InlineUseCaseService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { InlineUseCaseGenerator, InlineUseCaseScope, InlineUseCaseSession, Logger, ModelFlavor, ModelProvider, ModelProviderResolver, ModelRef } from '@cat-factory/kernel';
|
|
2
|
+
/**
|
|
3
|
+
* How long one invocation may wait on the vendor, by default.
|
|
4
|
+
*
|
|
5
|
+
* Generous, because the ceiling on `maxOutputTokens` is 32,000 and a long scene legitimately takes
|
|
6
|
+
* minutes, but FINITE: the alternative is a request held open for as long as the transport allows,
|
|
7
|
+
* on a surface whose whole shape is "ask and be answered". A deployment narrows it per facade.
|
|
8
|
+
*/
|
|
9
|
+
export declare const DEFAULT_USE_CASE_TIMEOUT_MS = 120000;
|
|
10
|
+
/** What the generator needs to resolve its models and reach the provider. */
|
|
11
|
+
export interface LlmInlineUseCaseGeneratorDeps {
|
|
12
|
+
/** Resolve a ModelProvider for a workspace's credential scope (preferred). */
|
|
13
|
+
modelProviderResolver?: ModelProviderResolver;
|
|
14
|
+
/** Static provider (e.g. a fake in tests) used when no resolver is set. */
|
|
15
|
+
modelProvider?: ModelProvider;
|
|
16
|
+
/** Resolve a model catalog id to a ref, under the deployment's route order. */
|
|
17
|
+
resolveBlockModel?: (modelId: string | undefined, providerPreference?: readonly ModelFlavor[]) => ModelRef | undefined;
|
|
18
|
+
/** Keep an ambient-eligible harness ref inline (local mode) instead of refusing it. */
|
|
19
|
+
runsInline?: (ref: ModelRef) => boolean;
|
|
20
|
+
/** The per-invocation deadline; absent ⇒ {@link DEFAULT_USE_CASE_TIMEOUT_MS}. */
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
/** Facade logger; a failed generation with no trace is an unowned bug. */
|
|
23
|
+
logger?: Logger;
|
|
24
|
+
}
|
|
25
|
+
export declare class LlmInlineUseCaseGenerator implements InlineUseCaseGenerator {
|
|
26
|
+
private readonly deps;
|
|
27
|
+
constructor(deps: LlmInlineUseCaseGeneratorDeps);
|
|
28
|
+
/** Whether a generation can run at all (some provider is wired). */
|
|
29
|
+
get enabled(): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Resolve this request's credential pool ONCE and hand back the session that reads it.
|
|
32
|
+
*
|
|
33
|
+
* Not caught here: a pool that could not be read is a fact about the deployment, and each caller
|
|
34
|
+
* answers it differently (discovery says so per option and still publishes the catalog; an
|
|
35
|
+
* invocation lets it propagate). Swallowing it here would make both of them guess.
|
|
36
|
+
*/
|
|
37
|
+
forScope(scope: InlineUseCaseScope): Promise<InlineUseCaseSession>;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=LlmInlineUseCaseGenerator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LlmInlineUseCaseGenerator.d.ts","sourceRoot":"","sources":["../../../src/modules/useCases/LlmInlineUseCaseGenerator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,sBAAsB,EAGtB,kBAAkB,EAClB,oBAAoB,EACpB,MAAM,EACN,WAAW,EACX,aAAa,EACb,qBAAqB,EACrB,QAAQ,EAET,MAAM,qBAAqB,CAAA;AAqC5B;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,SAAU,CAAA;AAWlD,6EAA6E;AAC7E,MAAM,WAAW,6BAA6B;IAC5C,8EAA8E;IAC9E,qBAAqB,CAAC,EAAE,qBAAqB,CAAA;IAC7C,2EAA2E;IAC3E,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B,+EAA+E;IAC/E,iBAAiB,CAAC,EAAE,CAClB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,kBAAkB,CAAC,EAAE,SAAS,WAAW,EAAE,KACxC,QAAQ,GAAG,SAAS,CAAA;IACzB,uFAAuF;IACvF,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,OAAO,CAAA;IACvC,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AA+KD,qBAAa,yBAA0B,YAAW,sBAAsB;IAC1D,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAjC,YAA6B,IAAI,EAAE,6BAA6B,EAAI;IAEpE,oEAAoE;IACpE,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;;;;;OAMG;IACG,QAAQ,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAGvE;CACF"}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { generateText } from 'ai';
|
|
2
|
+
import { describeError, getErrorMessage, resolveScopedModelProvider, runsOnSubscriptionHarness, UnavailableError, } from '@cat-factory/kernel';
|
|
3
|
+
import { catFactoryObservability } from '@cat-factory/agents';
|
|
4
|
+
import { readUseCaseUsage } from './useCaseUsage.js';
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// The default {@link InlineUseCaseGenerator}: the inline LLM call behind an invocation of a
|
|
7
|
+
// registered use case.
|
|
8
|
+
//
|
|
9
|
+
// Structurally the `BugHuntAssessorService` twin, and for the same reason: an un-run-scoped inline
|
|
10
|
+
// call built from the model dependencies every facade already wires, so the feature needs no
|
|
11
|
+
// per-facade wiring of its own and the conformance harness swaps in a deterministic fake through
|
|
12
|
+
// the `InlineUseCaseGenerator` seam.
|
|
13
|
+
//
|
|
14
|
+
// Three things differ from every other inline caller. Two follow from the use case being a DECLARED
|
|
15
|
+
// narrowing rather than an engine step:
|
|
16
|
+
//
|
|
17
|
+
// - There is no block, no task and no preset consult. A use case names its own models, so nothing
|
|
18
|
+
// a workspace pinned elsewhere may reach in and change which model runs; the workspace's per-kind
|
|
19
|
+
// preset default would be exactly such a substitution.
|
|
20
|
+
// - A subscription-harness ref is REFUSED rather than degraded. Every other inline site degrades
|
|
21
|
+
// one to the routing default (`inlineModelRef`), which is right where the model is an
|
|
22
|
+
// implementation detail of a step. Here the model IS the request.
|
|
23
|
+
//
|
|
24
|
+
// The third follows from the surface being SYNCHRONOUS: the call is bounded by a deadline and one
|
|
25
|
+
// retry. Every other long-running model path in this repo is a dispatched job with a poll and a
|
|
26
|
+
// watchdog; this one is a request the caller is holding open, so an unbounded vendor stall would be
|
|
27
|
+
// paid for by whoever asked.
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
/**
|
|
30
|
+
* How long one invocation may wait on the vendor, by default.
|
|
31
|
+
*
|
|
32
|
+
* Generous, because the ceiling on `maxOutputTokens` is 32,000 and a long scene legitimately takes
|
|
33
|
+
* minutes, but FINITE: the alternative is a request held open for as long as the transport allows,
|
|
34
|
+
* on a surface whose whole shape is "ask and be answered". A deployment narrows it per facade.
|
|
35
|
+
*/
|
|
36
|
+
export const DEFAULT_USE_CASE_TIMEOUT_MS = 120_000;
|
|
37
|
+
/**
|
|
38
|
+
* Retries the AI SDK may make inside one invocation.
|
|
39
|
+
*
|
|
40
|
+
* ONE, against the SDK's default of two: a transient 429 or 502 is worth a second attempt, and the
|
|
41
|
+
* third would be spent inside a deadline the caller is already waiting out. The deadline covers all
|
|
42
|
+
* attempts together (one signal, one absolute expiry), so retrying cannot extend it.
|
|
43
|
+
*/
|
|
44
|
+
const USE_CASE_MAX_RETRIES = 1;
|
|
45
|
+
/**
|
|
46
|
+
* The AI SDK's finish reasons, mapped to the bounded wire class.
|
|
47
|
+
*
|
|
48
|
+
* `tool-calls` cannot occur (no tools are passed) and the SDK's `unknown`/`error` both mean the
|
|
49
|
+
* provider said nothing usable about why it stopped, so they land on `other` rather than on a
|
|
50
|
+
* fabricated `stop`. Reporting `stop` for an unknown finish would tell a caller the text is
|
|
51
|
+
* complete when nothing said so.
|
|
52
|
+
*/
|
|
53
|
+
function mapFinishReason(reason) {
|
|
54
|
+
switch (reason) {
|
|
55
|
+
case 'stop':
|
|
56
|
+
return 'stop';
|
|
57
|
+
case 'length':
|
|
58
|
+
return 'length';
|
|
59
|
+
case 'content-filter':
|
|
60
|
+
return 'content-filter';
|
|
61
|
+
default:
|
|
62
|
+
return 'other';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The credential scope, as the model-provider resolver takes it.
|
|
67
|
+
*
|
|
68
|
+
* An absent account or user is OMITTED rather than passed as null, because the two are different
|
|
69
|
+
* instructions to the pool: omitted means "resolve the workspace's owning account yourself", while
|
|
70
|
+
* an explicit null means "this pool has no account tier". Passing null for an unknown account would
|
|
71
|
+
* silently drop every account-scoped key.
|
|
72
|
+
*/
|
|
73
|
+
function modelScopeFor(scope) {
|
|
74
|
+
return {
|
|
75
|
+
workspaceId: scope.workspaceId,
|
|
76
|
+
...(scope.accountId ? { accountId: scope.accountId } : {}),
|
|
77
|
+
...(scope.userId ? { userId: scope.userId } : {}),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* One request's generator: the credential pool, resolved once, plus the two answers that read it.
|
|
82
|
+
*
|
|
83
|
+
* Everything expensive happened in {@link LlmInlineUseCaseGenerator.forScope}, which is what makes
|
|
84
|
+
* `availability` synchronous. A discovery read over a catalog therefore probes every declared
|
|
85
|
+
* option for free, where a per-option resolution meant an `accountOf` read, a configured-providers
|
|
86
|
+
* read and a key LEASE (an atomic select-and-mark write plus a decrypt) each time.
|
|
87
|
+
*/
|
|
88
|
+
class ScopedInlineUseCaseSession {
|
|
89
|
+
deps;
|
|
90
|
+
scope;
|
|
91
|
+
provider;
|
|
92
|
+
constructor(deps, scope, provider) {
|
|
93
|
+
this.deps = deps;
|
|
94
|
+
this.scope = scope;
|
|
95
|
+
this.provider = provider;
|
|
96
|
+
}
|
|
97
|
+
availability(option) {
|
|
98
|
+
const ref = this.refFor(option);
|
|
99
|
+
if (!ref)
|
|
100
|
+
return { available: false, reason: 'provider_unavailable' };
|
|
101
|
+
if (runsOnSubscriptionHarness(ref) && !this.deps.runsInline?.(ref)) {
|
|
102
|
+
// A subscription harness runs inside a per-run container against a pooled OAuth token. A use
|
|
103
|
+
// case has no container and no run, so this is a permanent property of the pairing rather
|
|
104
|
+
// than a missing credential, and it is reported as its own cause. The test goes through
|
|
105
|
+
// kernel's `runsOnSubscriptionHarness` rather than a local spelling of it, because a ref
|
|
106
|
+
// carrying `harness: 'pi'` is the case a bare truthiness test gets wrong.
|
|
107
|
+
return { available: false, reason: 'container_only' };
|
|
108
|
+
}
|
|
109
|
+
if (!this.provider)
|
|
110
|
+
return { available: false, reason: 'provider_unavailable' };
|
|
111
|
+
try {
|
|
112
|
+
this.provider.resolve(ref);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
// `resolve` throws for a provider id with no registered resolver, which on this platform means
|
|
116
|
+
// the deployment configured no credentials for it. Logged rather than swallowed: an operator
|
|
117
|
+
// reading "unavailable" on a model they believe they configured needs the resolver's own
|
|
118
|
+
// message, which names what IS registered.
|
|
119
|
+
this.deps.logger?.debug('A use-case model did not resolve', {
|
|
120
|
+
workspaceId: this.scope.workspaceId,
|
|
121
|
+
model: option.id,
|
|
122
|
+
...describeError(error),
|
|
123
|
+
});
|
|
124
|
+
return { available: false, reason: 'provider_unavailable' };
|
|
125
|
+
}
|
|
126
|
+
return { available: true, ref };
|
|
127
|
+
}
|
|
128
|
+
async generate(request) {
|
|
129
|
+
// Re-checked rather than trusted, and it costs nothing now that the pool is in hand: the port
|
|
130
|
+
// is called by the service AND directly by anything else holding a session, so `generate` owns
|
|
131
|
+
// the same refusal rather than assuming a caller made it.
|
|
132
|
+
const availability = this.availability(request.option);
|
|
133
|
+
if (!availability.available) {
|
|
134
|
+
throw new UnavailableError(`The model '${request.option.label}' cannot be served by this deployment`, 'use_case_model_unavailable', { model: request.option.id, cause: availability.reason });
|
|
135
|
+
}
|
|
136
|
+
const provider = this.provider;
|
|
137
|
+
if (!provider) {
|
|
138
|
+
throw new UnavailableError('No model provider is configured, so use cases cannot be invoked', 'use_case_models_unconfigured');
|
|
139
|
+
}
|
|
140
|
+
const timeoutMs = this.deps.timeoutMs ?? DEFAULT_USE_CASE_TIMEOUT_MS;
|
|
141
|
+
// ONE signal for the whole call, retries included, so the deadline is an absolute expiry rather
|
|
142
|
+
// than a per-attempt one. `aborted` is then what tells a timeout from a vendor error, without
|
|
143
|
+
// sniffing an error shape the SDK is free to change.
|
|
144
|
+
const deadline = AbortSignal.timeout(timeoutMs);
|
|
145
|
+
try {
|
|
146
|
+
const result = await generateText({
|
|
147
|
+
model: provider.resolve(availability.ref),
|
|
148
|
+
system: request.system,
|
|
149
|
+
prompt: request.prompt,
|
|
150
|
+
temperature: request.temperature,
|
|
151
|
+
maxOutputTokens: request.maxOutputTokens,
|
|
152
|
+
abortSignal: deadline,
|
|
153
|
+
maxRetries: USE_CASE_MAX_RETRIES,
|
|
154
|
+
// The call is tagged with the use case as its agent kind, so its tokens land in the same
|
|
155
|
+
// rollups every other inline call does and an operator can see what an editor is spending
|
|
156
|
+
// per use case rather than as one undifferentiated "inline" bucket.
|
|
157
|
+
providerOptions: catFactoryObservability({
|
|
158
|
+
agentKind: request.useCaseId,
|
|
159
|
+
workspaceId: this.scope.workspaceId,
|
|
160
|
+
}),
|
|
161
|
+
});
|
|
162
|
+
return {
|
|
163
|
+
text: result.text,
|
|
164
|
+
finishReason: mapFinishReason(result.finishReason),
|
|
165
|
+
usage: readUseCaseUsage(result.usage),
|
|
166
|
+
ref: availability.ref,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
throw this.failed(request, error, deadline.aborted ? timeoutMs : undefined);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* The refusal for a call the vendor did not complete.
|
|
175
|
+
*
|
|
176
|
+
* A timeout is its OWN reason rather than one more generation failure, because the caller's move
|
|
177
|
+
* differs: a failed call is worth surfacing to whoever asked, a call that ran out of time is
|
|
178
|
+
* worth retrying with a smaller reply budget. Collapsing them would hide the only one of the two
|
|
179
|
+
* that the caller can do something about.
|
|
180
|
+
*/
|
|
181
|
+
failed(request, error, timedOutAfterMs) {
|
|
182
|
+
const message = timedOutAfterMs === undefined
|
|
183
|
+
? `The use-case model '${request.option.label}' failed: ${getErrorMessage(error)}`
|
|
184
|
+
: `The use-case model '${request.option.label}' did not answer within ${timedOutAfterMs}ms`;
|
|
185
|
+
this.deps.logger?.warn(message, {
|
|
186
|
+
workspaceId: this.scope.workspaceId,
|
|
187
|
+
useCaseId: request.useCaseId,
|
|
188
|
+
model: request.option.id,
|
|
189
|
+
...describeError(error),
|
|
190
|
+
});
|
|
191
|
+
return new UnavailableError(message, timedOutAfterMs === undefined ? 'use_case_generation_failed' : 'use_case_generation_timeout', { model: request.option.id });
|
|
192
|
+
}
|
|
193
|
+
/** The ref one declared option resolves to, or undefined when this deployment cannot serve it. */
|
|
194
|
+
refFor(option) {
|
|
195
|
+
if (option.source.kind === 'provider')
|
|
196
|
+
return option.source.ref;
|
|
197
|
+
// A catalog id resolves under the DEPLOYMENT's default route order rather than a preset's: a
|
|
198
|
+
// use case is workspace-agnostic by construction (nothing about it is stored per workspace), so
|
|
199
|
+
// there is no preset in force to read an order off.
|
|
200
|
+
return this.deps.resolveBlockModel?.(option.source.modelId);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
export class LlmInlineUseCaseGenerator {
|
|
204
|
+
deps;
|
|
205
|
+
constructor(deps) {
|
|
206
|
+
this.deps = deps;
|
|
207
|
+
}
|
|
208
|
+
/** Whether a generation can run at all (some provider is wired). */
|
|
209
|
+
get enabled() {
|
|
210
|
+
return !!this.deps.modelProviderResolver || !!this.deps.modelProvider;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Resolve this request's credential pool ONCE and hand back the session that reads it.
|
|
214
|
+
*
|
|
215
|
+
* Not caught here: a pool that could not be read is a fact about the deployment, and each caller
|
|
216
|
+
* answers it differently (discovery says so per option and still publishes the catalog; an
|
|
217
|
+
* invocation lets it propagate). Swallowing it here would make both of them guess.
|
|
218
|
+
*/
|
|
219
|
+
async forScope(scope) {
|
|
220
|
+
const provider = await resolveScopedModelProvider(modelScopeFor(scope), this.deps);
|
|
221
|
+
return new ScopedInlineUseCaseSession(this.deps, scope, provider);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=LlmInlineUseCaseGenerator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LlmInlineUseCaseGenerator.js","sourceRoot":"","sources":["../../../src/modules/useCases/LlmInlineUseCaseGenerator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { UseCaseUsage } from '@cat-factory/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* The invocation's usage, as the public surface publishes it.
|
|
4
|
+
*
|
|
5
|
+
* The billed input is taken from the vendor's RAW payload through the shared reconciler, and the
|
|
6
|
+
* SDK's own flat total is the floor rather than the alternative: whichever is larger is the one
|
|
7
|
+
* that cannot be an understatement, and on every provider mapping in this build the two agree
|
|
8
|
+
* (each already folds both cache classes into its total). The floor is what keeps a provider that
|
|
9
|
+
* passes no `raw` through, or one whose payload this build does not recognise, from publishing a 0.
|
|
10
|
+
*
|
|
11
|
+
* `totalTokens` is the SUM rather than any total the provider reported, so the three numbers a
|
|
12
|
+
* caller receives always add up. A published total that disagreed with its own two parts would
|
|
13
|
+
* leave a consumer no way to tell which of them to trust.
|
|
14
|
+
*/
|
|
15
|
+
export declare function readUseCaseUsage(usage: unknown): UseCaseUsage;
|
|
16
|
+
//# sourceMappingURL=useCaseUsage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCaseUsage.d.ts","sourceRoot":"","sources":["../../../src/modules/useCases/useCaseUsage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAsB1D;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CAS7D"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { readInputTokenClasses } from '@cat-factory/agents';
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// What one inline use-case invocation cost, read off the AI SDK's usage object into the three
|
|
4
|
+
// numbers the public surface publishes.
|
|
5
|
+
//
|
|
6
|
+
// Its own module with its own test because the reading is NOT the obvious field read. The SDK's
|
|
7
|
+
// flat `inputTokens` is whatever the provider's own mapping put there, and vendors disagree about
|
|
8
|
+
// whether a cached prefix is INSIDE the prompt count (OpenAI: `prompt_tokens` covers it) or BESIDE
|
|
9
|
+
// it (Anthropic: `input_tokens` is fresh-only, with `cache_read_input_tokens` and
|
|
10
|
+
// `cache_creation_input_tokens` alongside). This repo already owns that reconciliation once, in
|
|
11
|
+
// `readInputTokenClasses`, and it is deliberately read here rather than re-spelled: a hand-rolled
|
|
12
|
+
// `usage.inputTokens ?? usage.promptTokens` understates a cache-heavy call on half the vendors,
|
|
13
|
+
// and the surface publishes that number as what was BILLED, which a wrapper metering its own users
|
|
14
|
+
// would then under-bill from silently.
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
/** A usable token count, else 0. Never coerced, never negative. */
|
|
17
|
+
function count(value) {
|
|
18
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : 0;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The invocation's usage, as the public surface publishes it.
|
|
22
|
+
*
|
|
23
|
+
* The billed input is taken from the vendor's RAW payload through the shared reconciler, and the
|
|
24
|
+
* SDK's own flat total is the floor rather than the alternative: whichever is larger is the one
|
|
25
|
+
* that cannot be an understatement, and on every provider mapping in this build the two agree
|
|
26
|
+
* (each already folds both cache classes into its total). The floor is what keeps a provider that
|
|
27
|
+
* passes no `raw` through, or one whose payload this build does not recognise, from publishing a 0.
|
|
28
|
+
*
|
|
29
|
+
* `totalTokens` is the SUM rather than any total the provider reported, so the three numbers a
|
|
30
|
+
* caller receives always add up. A published total that disagreed with its own two parts would
|
|
31
|
+
* leave a consumer no way to tell which of them to trust.
|
|
32
|
+
*/
|
|
33
|
+
export function readUseCaseUsage(usage) {
|
|
34
|
+
const reported = (usage ?? {});
|
|
35
|
+
const classes = readInputTokenClasses(reported.raw);
|
|
36
|
+
const inputTokens = Math.max(classes.fresh + classes.cacheRead + classes.cacheWrite, count(reported.inputTokens));
|
|
37
|
+
const outputTokens = count(reported.outputTokens);
|
|
38
|
+
return { inputTokens, outputTokens, totalTokens: inputTokens + outputTokens };
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=useCaseUsage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCaseUsage.js","sourceRoot":"","sources":["../../../src/modules/useCases/useCaseUsage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAG3D,8EAA8E;AAC9E,8FAA8F;AAC9F,wCAAwC;AACxC,EAAE;AACF,gGAAgG;AAChG,kGAAkG;AAClG,mGAAmG;AACnG,kFAAkF;AAClF,gGAAgG;AAChG,kGAAkG;AAClG,gGAAgG;AAChG,mGAAmG;AACnG,uCAAuC;AACvC,8EAA8E;AAE9E,mEAAmE;AACnE,SAAS,KAAK,CAAC,KAAc;IAC3B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;AACrF,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,MAAM,QAAQ,GAAG,CAAC,KAAK,IAAI,EAAE,CAA4B,CAAA;IACzD,MAAM,OAAO,GAAG,qBAAqB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;IACnD,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAC1B,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,UAAU,EACtD,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,CAC5B,CAAA;IACD,MAAM,YAAY,GAAG,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAA;IACjD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,GAAG,YAAY,EAAE,CAAA;AAC/E,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { InlineUseCaseDefinition } from '@cat-factory/kernel';
|
|
2
|
+
import type { RegistrationProblem } from './validateRegistrations.js';
|
|
3
|
+
/**
|
|
4
|
+
* Deployment-registered INLINE USE CASES: every way a registration is broken that nothing at run
|
|
5
|
+
* time can recover from, minus its parameter form (which its host still runs the SHARED descriptor
|
|
6
|
+
* checker over, alongside every other registered form).
|
|
7
|
+
*
|
|
8
|
+
* Its own module rather than more of `validateRegistrations.ts` for the reason the binary-generator
|
|
9
|
+
* section has one: a cohesive concern with a growing rule set, and a host at its size ratchet.
|
|
10
|
+
*
|
|
11
|
+
* All errors, by the bar the rest of that validator uses: each is fully knowable from the
|
|
12
|
+
* registration itself. Boot is also the only door these ever reach. A use case is code, so no write
|
|
13
|
+
* boundary refuses it, and every fault below is either silent or MISATTRIBUTED at request time:
|
|
14
|
+
*
|
|
15
|
+
* - a malformed id is unaddressable, because the id IS the path segment;
|
|
16
|
+
* - an empty or ambiguously-defaulted model list means an invocation naming no model either cannot
|
|
17
|
+
* resolve one or resolves whichever the author happened to list first, which is exactly the
|
|
18
|
+
* substitution the narrowing exists to prevent;
|
|
19
|
+
* - a bound whose default sits outside its own range refuses every invocation that omits the knob,
|
|
20
|
+
* naming a value the caller never sent;
|
|
21
|
+
* - a catalog model id nothing resolves publishes as `provider_unavailable`, whose documented
|
|
22
|
+
* remedy is "configure the provider": the operator then hunts a key for a model that will never
|
|
23
|
+
* resolve, and the two-member reason vocabulary the surface publishes cannot say otherwise;
|
|
24
|
+
* - a caption outside the PUBLISHED bounds serves a shape the surface's own OpenAPI calls
|
|
25
|
+
* impossible, and silently, because a response is not re-validated on the way out;
|
|
26
|
+
* - a blank `systemPrompt` is the one the type comment already argues about: a use case with no
|
|
27
|
+
* instruction is an unrestricted model call wearing a name.
|
|
28
|
+
*/
|
|
29
|
+
export declare function inlineUseCaseProblems(useCase: InlineUseCaseDefinition): RegistrationProblem[];
|
|
30
|
+
//# sourceMappingURL=validateInlineUseCases.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateInlineUseCases.d.ts","sourceRoot":"","sources":["../../src/validation/validateInlineUseCases.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;AAKlE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAErE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,uBAAuB,GAAG,mBAAmB,EAAE,CAgB7F"}
|