@fgv/ts-extras 5.1.0-56 → 5.1.0-57
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/CAPABILITIES.md +3 -2
- package/dist/packlets/ai-assist/chatRequestBuilders.js +2 -2
- package/dist/packlets/ai-assist/chatRequestBuilders.js.map +1 -1
- package/dist/packlets/ai-assist/completionClient.js +74 -14
- package/dist/packlets/ai-assist/completionClient.js.map +1 -1
- package/dist/packlets/ai-assist/converters.js +54 -0
- package/dist/packlets/ai-assist/converters.js.map +1 -1
- package/dist/packlets/ai-assist/embeddingClient.js +16 -1
- package/dist/packlets/ai-assist/embeddingClient.js.map +1 -1
- package/dist/packlets/ai-assist/imageGenerationClient.js +16 -1
- package/dist/packlets/ai-assist/imageGenerationClient.js.map +1 -1
- package/dist/packlets/ai-assist/listModelsClient.js +19 -1
- package/dist/packlets/ai-assist/listModelsClient.js.map +1 -1
- package/dist/packlets/ai-assist/model.js.map +1 -1
- package/dist/packlets/ai-assist/streamUsageCapability.js +16 -6
- package/dist/packlets/ai-assist/streamUsageCapability.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/anthropic.js +8 -4
- package/dist/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +22 -7
- package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/common.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js +17 -9
- package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js +12 -9
- package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/proxy.js +62 -4
- package/dist/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -1
- package/dist/packlets/ai-assist/streamingClient.js +20 -6
- package/dist/packlets/ai-assist/streamingClient.js.map +1 -1
- package/dist/packlets/ai-assist/thinkingOptionsResolver.js +5 -2
- package/dist/packlets/ai-assist/thinkingOptionsResolver.js.map +1 -1
- package/dist/packlets/ai-assist/toolTypes.js.map +1 -1
- package/dist/ts-extras.d.ts +223 -24
- package/lib/packlets/ai-assist/chatRequestBuilders.d.ts +2 -2
- package/lib/packlets/ai-assist/chatRequestBuilders.js +2 -2
- package/lib/packlets/ai-assist/chatRequestBuilders.js.map +1 -1
- package/lib/packlets/ai-assist/completionClient.d.ts +13 -0
- package/lib/packlets/ai-assist/completionClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/completionClient.js +74 -14
- package/lib/packlets/ai-assist/completionClient.js.map +1 -1
- package/lib/packlets/ai-assist/converters.d.ts +46 -0
- package/lib/packlets/ai-assist/converters.d.ts.map +1 -1
- package/lib/packlets/ai-assist/converters.js +55 -1
- package/lib/packlets/ai-assist/converters.js.map +1 -1
- package/lib/packlets/ai-assist/embeddingClient.d.ts +9 -0
- package/lib/packlets/ai-assist/embeddingClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/embeddingClient.js +16 -1
- package/lib/packlets/ai-assist/embeddingClient.js.map +1 -1
- package/lib/packlets/ai-assist/imageGenerationClient.d.ts +9 -0
- package/lib/packlets/ai-assist/imageGenerationClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/imageGenerationClient.js +16 -1
- package/lib/packlets/ai-assist/imageGenerationClient.js.map +1 -1
- package/lib/packlets/ai-assist/listModelsClient.d.ts +12 -0
- package/lib/packlets/ai-assist/listModelsClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/listModelsClient.js +19 -1
- package/lib/packlets/ai-assist/listModelsClient.js.map +1 -1
- package/lib/packlets/ai-assist/model.d.ts +22 -7
- package/lib/packlets/ai-assist/model.d.ts.map +1 -1
- package/lib/packlets/ai-assist/model.js.map +1 -1
- package/lib/packlets/ai-assist/streamUsageCapability.d.ts +16 -6
- package/lib/packlets/ai-assist/streamUsageCapability.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamUsageCapability.js +16 -6
- package/lib/packlets/ai-assist/streamUsageCapability.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts +2 -1
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.js +6 -2
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts +37 -2
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +21 -6
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/common.d.ts +17 -3
- package/lib/packlets/ai-assist/streamingAdapters/common.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/common.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts +2 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js +15 -7
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts +2 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js +10 -7
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts +22 -0
- package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/proxy.js +62 -4
- package/lib/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -1
- package/lib/packlets/ai-assist/streamingClient.d.ts +5 -0
- package/lib/packlets/ai-assist/streamingClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingClient.js +19 -5
- package/lib/packlets/ai-assist/streamingClient.js.map +1 -1
- package/lib/packlets/ai-assist/thinkingOptionsResolver.d.ts +11 -3
- package/lib/packlets/ai-assist/thinkingOptionsResolver.d.ts.map +1 -1
- package/lib/packlets/ai-assist/thinkingOptionsResolver.js +5 -2
- package/lib/packlets/ai-assist/thinkingOptionsResolver.js.map +1 -1
- package/lib/packlets/ai-assist/toolTypes.d.ts +61 -3
- package/lib/packlets/ai-assist/toolTypes.d.ts.map +1 -1
- package/lib/packlets/ai-assist/toolTypes.js.map +1 -1
- package/package.json +7 -7
package/dist/ts-extras.d.ts
CHANGED
|
@@ -644,8 +644,11 @@ declare const ANTHROPIC_STRUCTURED_OUTPUT_TOOL_NAME: string;
|
|
|
644
644
|
*
|
|
645
645
|
* Policy: low = 2048, medium = 8192, high = 24000, max = 32000. The lower three
|
|
646
646
|
* align with the Anthropic-published minimum-meaningful budget, a mid-range
|
|
647
|
-
* default, and a "deep thinking" allotment respectively. `max`
|
|
648
|
-
*
|
|
647
|
+
* default, and a "deep thinking" allotment respectively. `max` is the deepest
|
|
648
|
+
* allotment and stays within typical model limits.
|
|
649
|
+
*
|
|
650
|
+
* Only reached for models outside the Claude 5 family — the adaptive-thinking path
|
|
651
|
+
* sends no budget at all.
|
|
649
652
|
*
|
|
650
653
|
* @public
|
|
651
654
|
*/
|
|
@@ -809,6 +812,11 @@ declare function callProviderCompletion(params: IProviderCompletionParams): Prom
|
|
|
809
812
|
* `Result.fail` before fetch is invoked. Callers should route through
|
|
810
813
|
* {@link AiAssist.callProxiedCompletionStream} or surface the failure to the user.
|
|
811
814
|
*
|
|
815
|
+
* This guard is applied **here only**. `executeClientToolTurn` is also a
|
|
816
|
+
* streaming entry point and does not pre-flight the flag, so a browser
|
|
817
|
+
* client-tool turn against a `streamingCorsRestricted` provider fails at the
|
|
818
|
+
* fetch with a CORS error rather than with this legible refusal.
|
|
819
|
+
*
|
|
812
820
|
* Connection-time failures (auth, network, non-2xx) surface as the outer
|
|
813
821
|
* `Result.fail`. Once iteration begins, errors mid-stream surface as a
|
|
814
822
|
* terminal error event ({@link AiAssist.IAiStreamError}) followed by the iterable
|
|
@@ -865,6 +873,19 @@ declare function callProviderListModels(params: IProviderListModelsParams): Prom
|
|
|
865
873
|
* CORS, and API key forwarding. The request body serializes the unified
|
|
866
874
|
* {@link AiAssist.IChatRequest} shape (`system?` + `messages`). Enforces the same
|
|
867
875
|
* non-empty / trailing-user-turn and image-input invariants as the direct path.
|
|
876
|
+
*
|
|
877
|
+
* @remarks
|
|
878
|
+
* Two parameters are handled before the body is composed, rather than forwarded.
|
|
879
|
+
* `tier` is resolved here and sent as a concrete `modelOverride`, because both
|
|
880
|
+
* halves of the resolution live on the descriptor the caller already holds — so a
|
|
881
|
+
* proxy needs no `tier` vocabulary, where a `tier` body field it did not understand
|
|
882
|
+
* would be ignored and silently serve a frontier request from the base model.
|
|
883
|
+
* `endpoint` is **refused**: it names where the prompt must go, the proxy is what
|
|
884
|
+
* makes that call, and the field has never been sent to one — so honoring it is
|
|
885
|
+
* unverifiable and ignoring it would reach the provider's default upstream instead
|
|
886
|
+
* of the host the caller pinned. Use `callProviderCompletion`, or point the
|
|
887
|
+
* proxy itself at the intended upstream.
|
|
888
|
+
*
|
|
868
889
|
* @param proxyUrl - Base URL of the proxy server
|
|
869
890
|
* @param params - Same parameters as {@link callProviderCompletion}
|
|
870
891
|
* @public
|
|
@@ -882,9 +903,23 @@ declare function callProxiedCompletion(proxyUrl: string, params: IProviderComple
|
|
|
882
903
|
* - Response: `Content-Type: text/event-stream`; body is the unified
|
|
883
904
|
* {@link AiAssist.IAiStreamEvent} JSON-serialized one event per SSE `data:` line
|
|
884
905
|
* (no `event:` line needed since the type discriminator is in the JSON).
|
|
906
|
+
* **Only `text-delta`, `tool-event`, `done` and `error` are carried** — an event
|
|
907
|
+
* of any other type, including the three `client-tool-*` variants, is discarded
|
|
908
|
+
* without error. Client-tool turns are not proxiable; `executeClientToolTurn`
|
|
909
|
+
* has no proxied entry point.
|
|
885
910
|
* - Error response (when the proxy can't even start): JSON `{error: string}`
|
|
886
911
|
* with a non-2xx status, surfaced as `proxy: ${error}`.
|
|
887
912
|
*
|
|
913
|
+
* Two request parameters are handled before the body is composed, identically to
|
|
914
|
+
* `callProxiedCompletion`: `tier` is resolved here and sent as a concrete
|
|
915
|
+
* `modelOverride`, so a proxy needs no `tier` vocabulary; `endpoint` is refused,
|
|
916
|
+
* because a proxy cannot confirm it honored it and reaching the provider's default
|
|
917
|
+
* upstream instead would send the request somewhere the caller excluded. `cache`,
|
|
918
|
+
* by contrast, is forwarded as a plain `body.cache` field exactly as
|
|
919
|
+
* `callProxiedCompletion` forwards it — a proxy that ignores it degrades to the
|
|
920
|
+
* request it would have sent anyway, never a misdirected one, so there is no
|
|
921
|
+
* `endpoint`-style reason to refuse it.
|
|
922
|
+
*
|
|
888
923
|
* The proxy server is responsible for opening the upstream SSE connection,
|
|
889
924
|
* translating provider-native events to the unified vocabulary, and
|
|
890
925
|
* forwarding events as they arrive (no buffering). The library does not
|
|
@@ -901,6 +936,15 @@ declare function callProxiedCompletionStream(proxyUrl: string, params: IProvider
|
|
|
901
936
|
* handles descriptor lookup, model/capability resolution, and provider dispatch.
|
|
902
937
|
* Error body `{ error: string }` is surfaced as `proxy: ${error}`.
|
|
903
938
|
*
|
|
939
|
+
* @remarks
|
|
940
|
+
* `endpoint` is **refused** on this path rather than forwarded or dropped, on the
|
|
941
|
+
* same terms as `callProxiedCompletion`. It matters more here than
|
|
942
|
+
* anywhere else: pointing `endpoint` at `http://localhost:11434/v1` is the
|
|
943
|
+
* documented way to reach a local Ollama, so dropping it does not degrade the
|
|
944
|
+
* answer — it sends the text to OpenAI's public API instead of the machine the
|
|
945
|
+
* caller named. Use `callProviderEmbedding`, or point the proxy
|
|
946
|
+
* itself at the intended upstream.
|
|
947
|
+
*
|
|
904
948
|
* @param proxyUrl - Base URL of the proxy server (e.g. `http://localhost:3001`).
|
|
905
949
|
* @param params - Same parameters as {@link AiAssist.callProviderEmbedding}.
|
|
906
950
|
* @returns The embedding result, or a failure.
|
|
@@ -916,6 +960,15 @@ declare function callProxiedEmbedding(proxyUrl: string, params: IProviderEmbeddi
|
|
|
916
960
|
* lookup, model resolution, provider dispatch, and response normalization
|
|
917
961
|
* (including repackaging `referenceImages` for the upstream wire format).
|
|
918
962
|
* Error body `{error: string}` is surfaced as `proxy: ${error}`.
|
|
963
|
+
*
|
|
964
|
+
* @remarks
|
|
965
|
+
* `endpoint` is **refused** on this path rather than forwarded or dropped, on the
|
|
966
|
+
* same terms as `callProxiedCompletion`: a proxy cannot confirm it honored
|
|
967
|
+
* it, the field has never been sent to one, and reaching the provider's default
|
|
968
|
+
* upstream instead would send the prompt somewhere the caller excluded. Use
|
|
969
|
+
* `callProviderImageGeneration`, or point the proxy itself at the intended
|
|
970
|
+
* upstream.
|
|
971
|
+
*
|
|
919
972
|
* @param proxyUrl - Base URL of the proxy server
|
|
920
973
|
* @param params - Same parameters as {@link callProviderImageGeneration}
|
|
921
974
|
* @public
|
|
@@ -927,6 +980,18 @@ declare function callProxiedImageGeneration(proxyUrl: string, params: IProviderI
|
|
|
927
980
|
* `POST ${proxyUrl}/api/ai/list-models`. Capability config is not forwarded;
|
|
928
981
|
* `capabilities` is serialized as a string array. Error body `{error: string}`
|
|
929
982
|
* is surfaced as `proxy: ${error}`.
|
|
983
|
+
*
|
|
984
|
+
* @remarks
|
|
985
|
+
* `capabilityConfig` and `endpoint` are both unusable here, but they are not the
|
|
986
|
+
* same case and are not treated the same way. `capabilityConfig` overrides how a
|
|
987
|
+
* model id is classified, and the proxy returns models already classified — so a
|
|
988
|
+
* caller-supplied override could only ever have arrived too late. Ignoring it
|
|
989
|
+
* yields the default classification: a degraded answer, disclosed above, and
|
|
990
|
+
* that is where it stays. `endpoint` names *which server answers at all*, so
|
|
991
|
+
* ignoring it would list the provider's public catalog in place of the host the
|
|
992
|
+
* caller pinned; it is **refused**, on the same terms as
|
|
993
|
+
* `callProxiedCompletion`.
|
|
994
|
+
*
|
|
930
995
|
* @public
|
|
931
996
|
*/
|
|
932
997
|
declare function callProxiedListModels(proxyUrl: string, params: IProviderListModelsParams): Promise<Result<ReadonlyArray<IAiModelInfo>>>;
|
|
@@ -2738,6 +2803,51 @@ declare interface IAiClientToolTurnResult {
|
|
|
2738
2803
|
* because the pinned model cannot ground and call functions in one request.
|
|
2739
2804
|
*/
|
|
2740
2805
|
readonly toolConflicts: IAiToolConflictReport;
|
|
2806
|
+
/**
|
|
2807
|
+
* Token usage for this turn's provider call, when the provider reports it.
|
|
2808
|
+
*
|
|
2809
|
+
* @remarks
|
|
2810
|
+
* The same `IAiCompletionUsage` the plain completion paths carry — normalized
|
|
2811
|
+
* cache reads/writes, output and total input tokens, and a `reports` level
|
|
2812
|
+
* saying which of those the provider is able to tell you. Sourced from the
|
|
2813
|
+
* terminal `done` event of the underlying stream, so it is exactly what a
|
|
2814
|
+
* caller watching `events` would see.
|
|
2815
|
+
*
|
|
2816
|
+
* Reading it here spares you *inspecting* the event stream, not *driving* it:
|
|
2817
|
+
* `nextTurn` only resolves once `events` has been iterated to completion, the
|
|
2818
|
+
* same as every other field on this interface.
|
|
2819
|
+
*
|
|
2820
|
+
* **One call, one usage.** `executeClientToolTurn` makes a single provider
|
|
2821
|
+
* call per invocation; the multi-round loop is driven by the caller
|
|
2822
|
+
* re-invoking with `continuationMessages`. So each result's `usage` already
|
|
2823
|
+
* *is* one round's usage, and a host accumulating a per-turn total sums the
|
|
2824
|
+
* results of its own loop. There is deliberately no `perRound` array here —
|
|
2825
|
+
* it could only ever have one entry.
|
|
2826
|
+
*
|
|
2827
|
+
* **Optional on purpose, unlike `toolConflicts`.** The library always knows
|
|
2828
|
+
* what it did to your tools, so a silent `toolConflicts` would be a defect
|
|
2829
|
+
* and it is required. It cannot always know what a turn cost, so a required
|
|
2830
|
+
* `usage` could only be satisfied by inventing one — the exact failure
|
|
2831
|
+
* {@link AiAssist.IAiCompletionUsage.reports} exists to prevent.
|
|
2832
|
+
*
|
|
2833
|
+
* Absent means **no normalized usage is being exposed**, which covers two
|
|
2834
|
+
* cases, the same as on {@link AiAssist.IAiCompletionResponse.usage} and
|
|
2835
|
+
* {@link AiAssist.IAiStreamDone.usage}:
|
|
2836
|
+
*
|
|
2837
|
+
* - the provider reported nothing; or
|
|
2838
|
+
* - the provider reported something the library declines to normalize,
|
|
2839
|
+
* because this descriptor is outside
|
|
2840
|
+
* {@link AiAssist.supportsCacheUsageReporting}. Groq, Mistral, Ollama and
|
|
2841
|
+
* self-hosted `openai-compat` share the OpenAI-format adapter but have
|
|
2842
|
+
* unverified usage shapes, so a raw `usage` block from one of them is
|
|
2843
|
+
* dropped rather than stamped with a cache-reporting level it may not
|
|
2844
|
+
* have earned.
|
|
2845
|
+
*
|
|
2846
|
+
* Either way it is never a fabricated zero. If you need to tell the two
|
|
2847
|
+
* apart, the descriptor answers it: the second case is a property of the
|
|
2848
|
+
* provider you picked, not of the turn.
|
|
2849
|
+
*/
|
|
2850
|
+
readonly usage?: IAiCompletionUsage;
|
|
2741
2851
|
}
|
|
2742
2852
|
|
|
2743
2853
|
/**
|
|
@@ -3564,11 +3674,23 @@ declare interface IAiToolEnablement {
|
|
|
3564
3674
|
*/
|
|
3565
3675
|
declare interface IAiWebSearchToolConfig {
|
|
3566
3676
|
readonly type: 'web_search';
|
|
3567
|
-
/**
|
|
3677
|
+
/**
|
|
3678
|
+
* Optional: restrict search to these domains.
|
|
3679
|
+
* Reaches Anthropic and the OpenAI/xAI Responses API; ignored on Gemini,
|
|
3680
|
+
* whose `google_search` tool takes no configuration at all.
|
|
3681
|
+
*/
|
|
3568
3682
|
readonly allowedDomains?: ReadonlyArray<string>;
|
|
3569
|
-
/**
|
|
3683
|
+
/**
|
|
3684
|
+
* Optional: exclude these domains from search.
|
|
3685
|
+
* Same reach as {@link AiAssist.IAiWebSearchToolConfig.allowedDomains}:
|
|
3686
|
+
* Anthropic and OpenAI/xAI, ignored on Gemini.
|
|
3687
|
+
*/
|
|
3570
3688
|
readonly blockedDomains?: ReadonlyArray<string>;
|
|
3571
|
-
/**
|
|
3689
|
+
/**
|
|
3690
|
+
* Optional: max number of searches per request.
|
|
3691
|
+
* **Anthropic only** — emitted as `max_uses` by the Anthropic formatter and
|
|
3692
|
+
* read nowhere else, so it is silently ignored on OpenAI/xAI and Gemini.
|
|
3693
|
+
*/
|
|
3572
3694
|
readonly maxUses?: number;
|
|
3573
3695
|
/**
|
|
3574
3696
|
* Optional: enable image understanding during web search.
|
|
@@ -3584,11 +3706,25 @@ declare interface IAiWebSearchToolConfig {
|
|
|
3584
3706
|
*/
|
|
3585
3707
|
declare interface IAnthropicThinkingConfig {
|
|
3586
3708
|
/**
|
|
3587
|
-
* Anthropic effort level
|
|
3588
|
-
*
|
|
3589
|
-
*
|
|
3590
|
-
* -
|
|
3591
|
-
*
|
|
3709
|
+
* Anthropic effort level, emitted as one of **two** wire shapes chosen by
|
|
3710
|
+
* {@link AiAssist.isAdaptiveThinkingModel}:
|
|
3711
|
+
*
|
|
3712
|
+
* - **Claude 5 family** (`claude-sonnet-5`, `claude-opus-5`, `claude-fable-5`
|
|
3713
|
+
* — which includes the `base` and `advanced` tiers, so this is the default
|
|
3714
|
+
* path): an adaptive `thinking` block plus an `output_config` block
|
|
3715
|
+
* carrying `effort`. The effort string goes on the wire verbatim and
|
|
3716
|
+
* **no token budget is sent**.
|
|
3717
|
+
* - **Older thinking-capable models**: an enabled `thinking` block carrying
|
|
3718
|
+
* `budget_tokens`, converted by `anthropicEffortToBudgetTokens` — low =
|
|
3719
|
+
* 2048, medium = 8192, high = 24000, max = 32000.
|
|
3720
|
+
*
|
|
3721
|
+
* So the budget mapping is not a description of what a default Anthropic
|
|
3722
|
+
* call sends; size a budget from it only when you have pinned a pre-Claude-5
|
|
3723
|
+
* model.
|
|
3724
|
+
*
|
|
3725
|
+
* `'max'` is accepted on every thinking-capable model here — the library
|
|
3726
|
+
* applies no model restriction of its own, and whether a given model accepts
|
|
3727
|
+
* it is the provider's to enforce.
|
|
3592
3728
|
*/
|
|
3593
3729
|
readonly effort?: 'low' | 'medium' | 'high' | 'max';
|
|
3594
3730
|
}
|
|
@@ -4565,6 +4701,17 @@ declare interface IExecuteClientToolTurnParams extends IChatRequest {
|
|
|
4565
4701
|
* pre-policy behavior: a `Result.fail` up front, before any wire call.
|
|
4566
4702
|
*/
|
|
4567
4703
|
readonly toolConflictPolicy?: AiToolConflictPolicy;
|
|
4704
|
+
/**
|
|
4705
|
+
* Prompt-caching plan for this request. Omitted, nothing cache-related is sent — the request
|
|
4706
|
+
* body is byte-identical to a build predating this feature. See {@link AiAssist.IAiCacheRequest}.
|
|
4707
|
+
*
|
|
4708
|
+
* @remarks
|
|
4709
|
+
* `executeClientToolTurn` represents a single round of a multi-round tool loop; a caller
|
|
4710
|
+
* driving several rounds passes this on **each** call, and it is validated against — and
|
|
4711
|
+
* applied to — that round's own `system` each time, never cached across rounds. Gated per
|
|
4712
|
+
* provider exactly as `IProviderCompletionParams.cache` is on the non-streaming path.
|
|
4713
|
+
*/
|
|
4714
|
+
readonly cache?: IAiCacheRequest;
|
|
4568
4715
|
}
|
|
4569
4716
|
|
|
4570
4717
|
/**
|
|
@@ -4574,14 +4721,37 @@ declare interface IExecuteClientToolTurnParams extends IChatRequest {
|
|
|
4574
4721
|
declare interface IExecuteClientToolTurnResult {
|
|
4575
4722
|
/**
|
|
4576
4723
|
* The unified-event iterable. Callers iterate this to drive the streaming UI.
|
|
4577
|
-
*
|
|
4578
|
-
*
|
|
4724
|
+
*
|
|
4725
|
+
* @remarks
|
|
4726
|
+
* Forwards every event the underlying adapter emits: `text-delta`,
|
|
4727
|
+
* `tool-event`, `done` and `error` come through from the provider stream, and
|
|
4728
|
+
* `client-tool-call-start`, `client-tool-call-done` and `client-tool-result`
|
|
4729
|
+
* are the tool-dispatch events this layer adds.
|
|
4730
|
+
*
|
|
4731
|
+
* The `done` event is the terminal one and carries
|
|
4732
|
+
* {@link AiAssist.IAiStreamDone.usage} when the provider reports it — this
|
|
4733
|
+
* iterable is not a text-only view. A caller that only wants the token counts
|
|
4734
|
+
* can read them off {@link AiAssist.IAiClientToolTurnResult.usage} instead of
|
|
4735
|
+
* matching on events — but it must still iterate this to completion, because
|
|
4736
|
+
* that is what drives `nextTurn` to resolve at all.
|
|
4737
|
+
*
|
|
4738
|
+
* (An earlier version of this comment listed only the five non-terminal
|
|
4739
|
+
* events. It was describing the tool-dispatch subset, but it read as the
|
|
4740
|
+
* complete set, and at least one consumer reasonably concluded from it that
|
|
4741
|
+
* usage never crossed the package boundary on this path. It always did.)
|
|
4579
4742
|
*/
|
|
4580
4743
|
readonly events: AsyncIterable<IAiStreamEvent>;
|
|
4581
4744
|
/**
|
|
4582
4745
|
* Resolves when the stream terminates. On success, carries the
|
|
4583
4746
|
* {@link AiAssist.IAiClientToolTurnResult} with the optional continuation for the
|
|
4584
4747
|
* next round. On failure, carries the error message.
|
|
4748
|
+
*
|
|
4749
|
+
* @remarks
|
|
4750
|
+
* **You must drive `events` for this to resolve.** It is settled from inside
|
|
4751
|
+
* the generator backing `events`, and an async generator does not start until
|
|
4752
|
+
* something iterates it — so `await nextTurn` on its own waits forever, and
|
|
4753
|
+
* the response body is never drained. Iterate `events` to completion (ignore
|
|
4754
|
+
* the events if you do not want them) and then await this.
|
|
4585
4755
|
*/
|
|
4586
4756
|
readonly nextTurn: Promise<Result<IAiClientToolTurnResult>>;
|
|
4587
4757
|
}
|
|
@@ -4674,8 +4844,9 @@ declare interface IGeminiThinkingConfig {
|
|
|
4674
4844
|
/**
|
|
4675
4845
|
* Whether to include thought summaries in the response.
|
|
4676
4846
|
* @remarks
|
|
4677
|
-
*
|
|
4678
|
-
*
|
|
4847
|
+
* Reserved for forward compatibility and **currently ignored by every
|
|
4848
|
+
* adapter** — nothing reads this field, so setting it has no effect on the
|
|
4849
|
+
* request. Surfacing thinking output as stream events is not yet built.
|
|
4679
4850
|
*/
|
|
4680
4851
|
readonly includeThoughts?: boolean;
|
|
4681
4852
|
}
|
|
@@ -5623,9 +5794,15 @@ declare interface IProviderCompletionParams extends IChatRequest {
|
|
|
5623
5794
|
}
|
|
5624
5795
|
|
|
5625
5796
|
/**
|
|
5626
|
-
* Parameters for a streaming completion request.
|
|
5627
|
-
*
|
|
5628
|
-
* so callers can be explicit about which path they're invoking.
|
|
5797
|
+
* Parameters for a streaming completion request. The non-streaming
|
|
5798
|
+
* `IProviderCompletionParams` shape **minus `structuredOutput`**; kept as its
|
|
5799
|
+
* own interface so callers can be explicit about which path they're invoking.
|
|
5800
|
+
*
|
|
5801
|
+
* @remarks
|
|
5802
|
+
* The omission is deliberate, not an oversight. Native structured output is a
|
|
5803
|
+
* non-streaming feature on every provider the package supports, so the field
|
|
5804
|
+
* would have nowhere to go here. A caller that needs it must use
|
|
5805
|
+
* `callProviderCompletion`.
|
|
5629
5806
|
*
|
|
5630
5807
|
* @remarks
|
|
5631
5808
|
* Carries the unified {@link AiAssist.IChatRequest} shape (`system?` + ordered
|
|
@@ -5681,6 +5858,13 @@ declare interface IProviderCompletionStreamParams extends IChatRequest {
|
|
|
5681
5858
|
* Messages API requires the field, so it falls back to `DEFAULT_ANTHROPIC_MAX_TOKENS`.
|
|
5682
5859
|
*/
|
|
5683
5860
|
readonly maxTokens?: number;
|
|
5861
|
+
/**
|
|
5862
|
+
* Prompt-caching plan for this request. Omitted, nothing cache-related is sent — the request
|
|
5863
|
+
* body is byte-identical to a build predating this feature. See {@link AiAssist.IAiCacheRequest}.
|
|
5864
|
+
* Validated against this request's own `system` at the point it is applied, and gated per
|
|
5865
|
+
* provider exactly as `IProviderCompletionParams.cache` is on the non-streaming path.
|
|
5866
|
+
*/
|
|
5867
|
+
readonly cache?: IAiCacheRequest;
|
|
5684
5868
|
}
|
|
5685
5869
|
|
|
5686
5870
|
/**
|
|
@@ -5919,7 +6103,12 @@ declare interface IResolvedImageOptions {
|
|
|
5919
6103
|
* @public
|
|
5920
6104
|
*/
|
|
5921
6105
|
declare interface IResolvedThinkingConfig {
|
|
5922
|
-
/**
|
|
6106
|
+
/**
|
|
6107
|
+
* Anthropic: effort level. The emit-site picks one of two wire shapes — on the
|
|
6108
|
+
* Claude 5 family it sends the effort string verbatim in `output_config` with no
|
|
6109
|
+
* budget; on older models it converts via `anthropicEffortToBudgetTokens`. See
|
|
6110
|
+
* {@link AiAssist.IAnthropicThinkingConfig.effort}.
|
|
6111
|
+
*/
|
|
5923
6112
|
readonly anthropicEffort?: IAnthropicThinkingConfig['effort'];
|
|
5924
6113
|
/** OpenAI Chat: reasoning_effort value; OpenAI Responses: reasoning.effort */
|
|
5925
6114
|
readonly openAiEffort?: IOpenAiThinkingConfig['effort'];
|
|
@@ -8822,9 +9011,10 @@ declare const SUPPORTED_SCHEMES: ReadonlyArray<string>;
|
|
|
8822
9011
|
* non-streaming alike).
|
|
8823
9012
|
*
|
|
8824
9013
|
* @remarks
|
|
8825
|
-
*
|
|
8826
|
-
*
|
|
8827
|
-
*
|
|
9014
|
+
* The completion paths it gates — `callOpenAiCompletion`,
|
|
9015
|
+
* `callOpenAiResponsesCompletion`, the Responses streaming adapter, and
|
|
9016
|
+
* `executeClientToolTurn`'s direct Responses call — are shared by every
|
|
9017
|
+
* `apiFormat: 'openai'` descriptor — xAI Grok, Groq, Mistral, Ollama, and self-hosted
|
|
8828
9018
|
* `openai-compat` all route through the same adapter as OpenAI. Only OpenAI
|
|
8829
9019
|
* and xAI Grok are confirmed to report cache-relevant `usage` fields; an
|
|
8830
9020
|
* ordinary `usage` block from one of the other descriptors (e.g. Groq's plain
|
|
@@ -8832,9 +9022,18 @@ declare const SUPPORTED_SCHEMES: ReadonlyArray<string>;
|
|
|
8832
9022
|
* carries no cache information. Normalizing it anyway would stamp
|
|
8833
9023
|
* `reports: 'reads'` on a provider that has no prompt-caching concept —
|
|
8834
9024
|
* a false cache-reporting signal, not an absent one. So this gate is checked
|
|
8835
|
-
* before every normalize call; when it's `false`, `usage` stays
|
|
8836
|
-
* entirely rather than being guessed from whatever shape the wire
|
|
8837
|
-
* send.
|
|
9025
|
+
* before every normalize call it governs; when it's `false`, `usage` stays
|
|
9026
|
+
* absent entirely rather than being guessed from whatever shape the wire
|
|
9027
|
+
* happens to send.
|
|
9028
|
+
*
|
|
9029
|
+
* **Streaming Chat Completions is gated by a different predicate.** That path
|
|
9030
|
+
* consults {@link AiAssist.supportsStreamUsageOption} instead, because the
|
|
9031
|
+
* request has to opt in via `stream_options: { include_usage: true }` before
|
|
9032
|
+
* there is any usage to normalize — and that opt-in is `'openai'`-only, since
|
|
9033
|
+
* the other descriptors' tolerance for the unrecognized field is unverified.
|
|
9034
|
+
* The practical consequence is that xAI Grok, which *is* confirmed here, still
|
|
9035
|
+
* gets no `usage` on streaming Chat Completions. If you are tracing a missing
|
|
9036
|
+
* xAI streaming figure, that predicate is the one to read, not this one.
|
|
8838
9037
|
* @public
|
|
8839
9038
|
*/
|
|
8840
9039
|
declare function supportsCacheUsageReporting(descriptor: IAiProviderDescriptor): boolean;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Per-format chat request shape builders. Shared between the synchronous
|
|
3
|
-
* (`
|
|
4
|
-
* shapes stay consistent.
|
|
3
|
+
* (`completionClient.ts`) and streaming (`streamingClient.ts`) paths so the
|
|
4
|
+
* wire shapes stay consistent.
|
|
5
5
|
*
|
|
6
6
|
* @packageDocumentation
|
|
7
7
|
*/
|
|
@@ -33,8 +33,8 @@ exports.buildAnthropicMessages = buildAnthropicMessages;
|
|
|
33
33
|
exports.buildGeminiContents = buildGeminiContents;
|
|
34
34
|
/**
|
|
35
35
|
* Per-format chat request shape builders. Shared between the synchronous
|
|
36
|
-
* (`
|
|
37
|
-
* shapes stay consistent.
|
|
36
|
+
* (`completionClient.ts`) and streaming (`streamingClient.ts`) paths so the
|
|
37
|
+
* wire shapes stay consistent.
|
|
38
38
|
*
|
|
39
39
|
* @packageDocumentation
|
|
40
40
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chatRequestBuilders.js","sourceRoot":"","sources":["../../../src/packlets/ai-assist/chatRequestBuilders.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Per-format chat request shape builders. Shared between the synchronous\n * (`apiClient.ts`) and streaming (`streamingClient.ts`) paths so the wire\n * shapes stay consistent.\n *\n * @packageDocumentation\n */\n\nimport { isJsonObject, type JsonObject } from '@fgv/ts-json-base';\nimport { type Converter, Converters, fail, Result, succeed } from '@fgv/ts-utils';\n\nimport { type IAiCacheRequest, splitSystemForCache } from './cacheRequest';\nimport { AiPrompt, type IAiImageAttachment, type IChatMessage, toDataUrl } from './model';\n\n/**\n * The result of splitting an {@link AiAssist.IChatRequest} into the per-builder\n * inputs: the current turn (as an {@link AiPrompt}, carrying system + the final\n * user message + any attachments) and the preceding conversation history.\n * @internal\n */\nexport interface ISplitChatRequest {\n /** The current turn lowered to an {@link AiPrompt} (system + user + attachments). */\n readonly prompt: AiPrompt;\n /** Prior conversation history — every message before the current turn. */\n readonly head: ReadonlyArray<IChatMessage>;\n}\n\n/**\n * Splits a unified {@link AiAssist.IChatRequest} into `{ prompt, head }` for the\n * per-provider builders. The **last** message is the current `user` turn; the\n * preceding messages are history (`head`). This is the single linearization\n * shared by every turn entry point, so the completion path and the client-tool\n * turn path place history at the identical position relative to the current turn.\n *\n * Fails when `messages` is empty (no current turn) or when the last message is\n * not a `user` turn — relabelling a trailing assistant message as the user turn\n * would be a silent footgun, so it is rejected loudly instead.\n *\n * @internal\n */\nexport function splitChatRequest(\n system: string | undefined,\n messages: ReadonlyArray<IChatMessage>\n): Result<ISplitChatRequest> {\n if (messages.length === 0) {\n return fail('messages must contain at least one entry (the current user turn)');\n }\n const current = messages[messages.length - 1];\n if (current.role !== 'user') {\n return fail(`the last message must be the current user turn (role 'user'); got '${current.role}'`);\n }\n const head = messages.slice(0, messages.length - 1);\n const prompt = new AiPrompt(current.content, system ?? '', current.attachments);\n return succeed({ prompt, head });\n}\n\n/**\n * Rebuilds the ordered messages for a proxy wire body so that only the current\n * turn can carry attachments — history (non-current) messages are reduced to\n * `{ role, content }`. The direct per-provider builders already drop attachments\n * on history turns (only the current user turn's attachments are honored), so\n * normalizing here keeps the proxy wire shape consistent with the direct paths\n * and avoids transmitting attachment payloads the upstream provider would ignore.\n *\n * @internal\n */\nexport function normalizeOutboundMessages(split: ISplitChatRequest): IChatMessage[] {\n return [\n ...split.head.map((m) => ({ role: m.role, content: m.content })),\n ...split.prompt.toRequest().messages\n ];\n}\n\n/**\n * Converter for a rawTail message entry. Narrows a `JsonObject` to\n * `{ role: string; content: string | unknown[] }` at runtime using the\n * Converter pattern. Entries that fail validation are silently skipped — the\n * surrounding function is infallible, and a malformed continuation message is\n * better omitted than transmitted verbatim.\n * @internal\n */\nconst rawTailMessageConverter: Converter<{ role: 'user' | 'assistant'; content: string | unknown[] }> =\n Converters.object<{ role: 'user' | 'assistant'; content: string | unknown[] }>(\n {\n role: Converters.enumeratedValue<'user' | 'assistant'>(['user', 'assistant']),\n content: Converters.oneOf<string | unknown[]>([\n Converters.string,\n Converters.isA('array', (v): v is unknown[] => Array.isArray(v))\n ])\n },\n { strict: false }\n );\n\n/**\n * Converter for an OpenAI / xAI Responses API `rawTail` item. These are\n * provider-native input items (`function_call`, `function_call_output`) whose\n * fields differ per item type, so — unlike the Anthropic `{ role, content }`\n * projection — the whole object is preserved verbatim.\n *\n * The static input is already typed `JsonObject`, so the `isJsonObject` guard\n * is a runtime backstop, not a compile-time narrowing: continuation messages\n * originate from a prior turn's `IAiClientToolContinuation.messages` and a\n * consumer may persist and reload them through untyped JSON before passing them\n * back. The guard preserves the same \"a malformed continuation message is\n * better omitted than transmitted verbatim\" posture as the Anthropic path —\n * non-object entries fail conversion and are skipped by the caller.\n * @internal\n */\nconst openAiRawTailItemConverter: Converter<JsonObject> = Converters.isA<JsonObject>(\n 'JsonObject',\n (v): v is JsonObject => isJsonObject(v)\n);\n\n/**\n * Converter for a Gemini `rawTail` item. Gemini continuation messages are\n * `{ role, parts }` turns (a model turn with `functionCall` parts followed by a\n * user turn with `functionResponse` parts). Narrows a `JsonObject` to\n * `{ role: 'user' | 'model'; parts: Array<Record<string, unknown>> }`; entries\n * that fail validation are skipped by the caller.\n * @internal\n */\nconst geminiRawTailMessageConverter: Converter<{\n role: 'user' | 'model';\n parts: unknown[];\n}> = Converters.object<{ role: 'user' | 'model'; parts: unknown[] }>(\n {\n role: Converters.enumeratedValue<'user' | 'model'>(['user', 'model']),\n // `parts` is preserved verbatim and serialized into the request body, so the\n // element shape is not narrowed here — `Array.isArray` soundly guarantees\n // `unknown[]` (narrowing to `Record<string, unknown>[]` would be an unchecked cast).\n parts: Converters.isA('array', (v): v is unknown[] => Array.isArray(v))\n },\n { strict: false }\n);\n\n/**\n * Optional history (`head`) and raw continuation (`rawTail`) messages to weave\n * around the prompt's current user message.\n *\n * @internal\n */\nexport interface IBuildMessagesOptions {\n /**\n * Prior conversation history inserted between the system prompt and the\n * prompt's current user message (multi-turn chat / correction retries). The\n * single ordered linearization is `[system, ...head, user, ...rawTail]`.\n */\n readonly head?: ReadonlyArray<IChatMessage>;\n /**\n * Raw JSON objects appended after the prompt's user message. Used to\n * inject provider-specific continuation messages (e.g. Anthropic assistant\n * turns with thinking blocks, OpenAI Responses `function_call` /\n * `function_call_output` items, Gemini `functionCall` / `functionResponse`\n * turns) that cannot be expressed as plain {@link IChatMessage} objects.\n *\n * Each builder applies its own provider-specific shape guard:\n * - {@link buildAnthropicMessages} projects each entry to `{ role, content }`.\n * - {@link buildMessages} (OpenAI / xAI Responses) preserves each item\n * verbatim (item fields differ per `type`), guarding only that it is a\n * JSON object.\n * - {@link buildGeminiContents} projects each entry to `{ role, parts }`.\n *\n * Entries that fail their builder's shape check are silently skipped (the\n * caller is responsible for supplying well-formed continuation messages).\n * Appended after the current user message.\n */\n readonly rawTail?: ReadonlyArray<JsonObject>;\n}\n\n/**\n * Builds the messages array from prompt + optional history (`head`) and raw\n * continuation (`rawTail`) messages. The caller supplies the system content\n * (string when no cache breakpoints are requested, parts array when they are\n * — see {@link buildOpenAiChatSystemContent} / {@link buildOpenAiResponsesSystemContent})\n * and the user content (string for text-only, parts array for vision prompts),\n * since both shapes differ by format.\n *\n * `rawTail` items (OpenAI / xAI Responses `function_call` /\n * `function_call_output` continuation items) are appended verbatim after the\n * user message — their fields differ per item `type`, so they are preserved\n * rather than projected. The return type is `Array<Record<string, unknown>>`\n * to accommodate both `{ role, content }` messages and these heterogeneous\n * input items.\n *\n * @internal\n */\nexport function buildMessages(\n systemContent: string | unknown[],\n userContent: string | unknown[],\n options?: IBuildMessagesOptions\n): Array<Record<string, unknown>> {\n const messages: Array<Record<string, unknown>> = [{ role: 'system', content: systemContent }];\n if (options?.head) {\n for (const msg of options.head) {\n messages.push({ role: msg.role, content: msg.content });\n }\n }\n messages.push({ role: 'user', content: userContent });\n // OpenAI / xAI Responses continuation items (function_call /\n // function_call_output) are appended verbatim — their field set differs per\n // item type, so the whole object is preserved rather than projected.\n if (options?.rawTail) {\n for (const item of options.rawTail) {\n const converted = openAiRawTailItemConverter.convert(item);\n if (converted.isSuccess()) {\n messages.push(converted.value);\n }\n }\n }\n return messages;\n}\n\n/**\n * Splits `segments` (from {@link splitSystemForCache}) into a single string when nothing carries\n * a breakpoint, or a content-part array in `partType`'s shape (with `prompt_cache_breakpoint` on\n * the parts that end a cacheable prefix) otherwise.\n *\n * @remarks\n * A single non-breakpointed segment (the common case: `cache` omitted or empty) returns the\n * plain string, byte-identical to a request built before cache support existed. Shared by\n * {@link buildOpenAiChatSystemContent} and {@link buildOpenAiResponsesSystemContent}, whose only\n * difference is the content-part `type` OpenAI's two APIs use (`'text'` vs `'input_text'`,\n * research.md §1.2).\n * @internal\n */\nfunction toOpenAiSystemContent(\n segments: ReadonlyArray<{ readonly text: string; readonly cacheBreakpoint: boolean }>,\n partType: 'text' | 'input_text'\n): string | unknown[] {\n if (segments.length === 1 && !segments[0].cacheBreakpoint) {\n return segments[0].text;\n }\n return segments.map((seg) => ({\n type: partType,\n text: seg.text,\n ...(seg.cacheBreakpoint\n ? {\n // eslint-disable-next-line @typescript-eslint/naming-convention -- wire field name\n prompt_cache_breakpoint: { mode: 'explicit' }\n }\n : {})\n }));\n}\n\n/**\n * Builds the system content for OpenAI Chat Completions, splitting `system` at `cache`'s\n * breakpoints (design.md §6.3) into content parts each carrying\n * `prompt_cache_breakpoint: { mode: 'explicit' }` where a cacheable prefix ends.\n * `prompt_cache_options` is never touched — OpenAI keeps its own implicit breakpoint and writes\n * up to the latest three of ours on top of it. Returns the plain `system` string when `cache` is\n * `undefined` or declares no breakpoints, byte-identical to a request built before cache support\n * existed.\n * @internal\n */\nexport function buildOpenAiChatSystemContent(\n system: string,\n cache: IAiCacheRequest | undefined\n): Result<string | unknown[]> {\n return splitSystemForCache(system, cache).onSuccess((segments) =>\n succeed(toOpenAiSystemContent(segments, 'text'))\n );\n}\n\n/**\n * Builds the system content for the OpenAI / xAI Responses API. Identical to\n * {@link buildOpenAiChatSystemContent} except for the content-part type (`'input_text'` instead\n * of `'text'`) — see research.md §1.2, \"Where the breakpoint may be attached.\"\n * @internal\n */\nexport function buildOpenAiResponsesSystemContent(\n system: string,\n cache: IAiCacheRequest | undefined\n): Result<string | unknown[]> {\n return splitSystemForCache(system, cache).onSuccess((segments) =>\n succeed(toOpenAiSystemContent(segments, 'input_text'))\n );\n}\n\n/**\n * Builds the Anthropic `system` field, splitting `system` at `cache`'s breakpoints into content\n * blocks carrying `cache_control: { type: 'ephemeral' }` on the block that ends each cacheable\n * prefix (design.md §6.1). Returns the plain `system` string when `cache` is `undefined` or\n * declares no breakpoints — byte-identical to `system: prompt.system` from a request built\n * before cache support existed.\n * @internal\n */\nexport function buildAnthropicSystem(\n system: string,\n cache: IAiCacheRequest | undefined\n): Result<string | unknown[]> {\n return splitSystemForCache(system, cache).onSuccess((segments) => {\n if (segments.length === 1 && !segments[0].cacheBreakpoint) {\n return succeed<string | unknown[]>(segments[0].text);\n }\n return succeed<string | unknown[]>(\n segments.map((seg) => ({\n type: 'text',\n text: seg.text,\n ...(seg.cacheBreakpoint ? { cache_control: { type: 'ephemeral' } } : {})\n }))\n );\n });\n}\n\n/**\n * Builds the user content for OpenAI Chat Completions when attachments are\n * present. Returns a string when there are no attachments.\n *\n * @internal\n */\nexport function buildOpenAiChatUserContent(prompt: AiPrompt): string | unknown[] {\n if (prompt.attachments.length === 0) {\n return prompt.user;\n }\n return [\n { type: 'text', text: prompt.user },\n ...prompt.attachments.map((att: IAiImageAttachment) => ({\n type: 'image_url',\n image_url: {\n url: toDataUrl(att),\n ...(att.detail !== undefined ? { detail: att.detail } : {})\n }\n }))\n ];\n}\n\n/**\n * Builds the user content for OpenAI / xAI Responses API when attachments\n * are present. Responses API uses `input_text` / `input_image` part types,\n * distinct from Chat Completions' `text` / `image_url`.\n *\n * @internal\n */\nexport function buildOpenAiResponsesUserContent(prompt: AiPrompt): string | unknown[] {\n if (prompt.attachments.length === 0) {\n return prompt.user;\n }\n return [\n { type: 'input_text', text: prompt.user },\n ...prompt.attachments.map((att: IAiImageAttachment) => ({\n type: 'input_image',\n image_url: toDataUrl(att),\n ...(att.detail !== undefined ? { detail: att.detail } : {})\n }))\n ];\n}\n\n/**\n * Builds the user-message content for Anthropic when attachments are present.\n *\n * @internal\n */\nexport function buildAnthropicUserContent(prompt: AiPrompt): string | unknown[] {\n if (prompt.attachments.length === 0) {\n return prompt.user;\n }\n return [\n { type: 'text', text: prompt.user },\n ...prompt.attachments.map((att: IAiImageAttachment) => ({\n type: 'image',\n source: {\n type: 'base64',\n media_type: att.mimeType,\n data: att.base64\n }\n }))\n ];\n}\n\n/**\n * Builds the Gemini `parts` array for the user turn, including any image\n * attachments as `inlineData` parts.\n *\n * @internal\n */\nexport function buildGeminiUserParts(prompt: AiPrompt): Array<Record<string, unknown>> {\n const parts: Array<Record<string, unknown>> = [{ text: prompt.user }];\n for (const att of prompt.attachments) {\n parts.push({ inlineData: { mimeType: att.mimeType, data: att.base64 } });\n }\n return parts;\n}\n\n/**\n * Builds the Anthropic messages array, weaving any `head` history messages\n * between implicit system + the prompt's user message and appending `rawTail`\n * continuation messages after. System messages are filtered out (Anthropic uses\n * a top-level system field).\n *\n * @internal\n */\nexport function buildAnthropicMessages(\n prompt: AiPrompt,\n options?: IBuildMessagesOptions\n): Array<{ role: string; content: string | unknown[] }> {\n const messages: Array<{ role: string; content: string | unknown[] }> = [];\n if (options?.head) {\n for (const msg of options.head) {\n if (msg.role !== 'system') {\n messages.push({ role: msg.role, content: msg.content });\n }\n }\n }\n messages.push({ role: 'user', content: buildAnthropicUserContent(prompt) });\n if (options?.rawTail) {\n for (const msg of options.rawTail) {\n const converted = rawTailMessageConverter.convert(msg);\n if (converted.isSuccess()) {\n messages.push(converted.value);\n }\n }\n }\n return messages;\n}\n\n/**\n * Builds the Gemini `contents` array, weaving any `head` history messages before\n * the prompt's user parts and appending `rawTail` continuation messages after.\n * System messages are filtered out (Gemini uses a top-level systemInstruction\n * field) and assistant roles are mapped to Gemini's `model` role.\n *\n * @internal\n */\nexport function buildGeminiContents(\n prompt: AiPrompt,\n options?: IBuildMessagesOptions\n): Array<{ role: string; parts: unknown[] }> {\n const contents: Array<{ role: string; parts: unknown[] }> = [];\n if (options?.head) {\n for (const msg of options.head) {\n if (msg.role !== 'system') {\n contents.push({\n role: msg.role === 'assistant' ? 'model' : msg.role,\n parts: [{ text: msg.content }]\n });\n }\n }\n }\n contents.push({ role: 'user', parts: buildGeminiUserParts(prompt) });\n // Gemini continuation turns (model `functionCall` parts + user\n // `functionResponse` parts) are projected to `{ role, parts }`.\n if (options?.rawTail) {\n for (const item of options.rawTail) {\n const converted = geminiRawTailMessageConverter.convert(item);\n if (converted.isSuccess()) {\n contents.push(converted.value);\n }\n }\n }\n return contents;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"chatRequestBuilders.js","sourceRoot":"","sources":["../../../src/packlets/ai-assist/chatRequestBuilders.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Per-format chat request shape builders. Shared between the synchronous\n * (`completionClient.ts`) and streaming (`streamingClient.ts`) paths so the\n * wire shapes stay consistent.\n *\n * @packageDocumentation\n */\n\nimport { isJsonObject, type JsonObject } from '@fgv/ts-json-base';\nimport { type Converter, Converters, fail, Result, succeed } from '@fgv/ts-utils';\n\nimport { type IAiCacheRequest, splitSystemForCache } from './cacheRequest';\nimport { AiPrompt, type IAiImageAttachment, type IChatMessage, toDataUrl } from './model';\n\n/**\n * The result of splitting an {@link AiAssist.IChatRequest} into the per-builder\n * inputs: the current turn (as an {@link AiPrompt}, carrying system + the final\n * user message + any attachments) and the preceding conversation history.\n * @internal\n */\nexport interface ISplitChatRequest {\n /** The current turn lowered to an {@link AiPrompt} (system + user + attachments). */\n readonly prompt: AiPrompt;\n /** Prior conversation history — every message before the current turn. */\n readonly head: ReadonlyArray<IChatMessage>;\n}\n\n/**\n * Splits a unified {@link AiAssist.IChatRequest} into `{ prompt, head }` for the\n * per-provider builders. The **last** message is the current `user` turn; the\n * preceding messages are history (`head`). This is the single linearization\n * shared by every turn entry point, so the completion path and the client-tool\n * turn path place history at the identical position relative to the current turn.\n *\n * Fails when `messages` is empty (no current turn) or when the last message is\n * not a `user` turn — relabelling a trailing assistant message as the user turn\n * would be a silent footgun, so it is rejected loudly instead.\n *\n * @internal\n */\nexport function splitChatRequest(\n system: string | undefined,\n messages: ReadonlyArray<IChatMessage>\n): Result<ISplitChatRequest> {\n if (messages.length === 0) {\n return fail('messages must contain at least one entry (the current user turn)');\n }\n const current = messages[messages.length - 1];\n if (current.role !== 'user') {\n return fail(`the last message must be the current user turn (role 'user'); got '${current.role}'`);\n }\n const head = messages.slice(0, messages.length - 1);\n const prompt = new AiPrompt(current.content, system ?? '', current.attachments);\n return succeed({ prompt, head });\n}\n\n/**\n * Rebuilds the ordered messages for a proxy wire body so that only the current\n * turn can carry attachments — history (non-current) messages are reduced to\n * `{ role, content }`. The direct per-provider builders already drop attachments\n * on history turns (only the current user turn's attachments are honored), so\n * normalizing here keeps the proxy wire shape consistent with the direct paths\n * and avoids transmitting attachment payloads the upstream provider would ignore.\n *\n * @internal\n */\nexport function normalizeOutboundMessages(split: ISplitChatRequest): IChatMessage[] {\n return [\n ...split.head.map((m) => ({ role: m.role, content: m.content })),\n ...split.prompt.toRequest().messages\n ];\n}\n\n/**\n * Converter for a rawTail message entry. Narrows a `JsonObject` to\n * `{ role: string; content: string | unknown[] }` at runtime using the\n * Converter pattern. Entries that fail validation are silently skipped — the\n * surrounding function is infallible, and a malformed continuation message is\n * better omitted than transmitted verbatim.\n * @internal\n */\nconst rawTailMessageConverter: Converter<{ role: 'user' | 'assistant'; content: string | unknown[] }> =\n Converters.object<{ role: 'user' | 'assistant'; content: string | unknown[] }>(\n {\n role: Converters.enumeratedValue<'user' | 'assistant'>(['user', 'assistant']),\n content: Converters.oneOf<string | unknown[]>([\n Converters.string,\n Converters.isA('array', (v): v is unknown[] => Array.isArray(v))\n ])\n },\n { strict: false }\n );\n\n/**\n * Converter for an OpenAI / xAI Responses API `rawTail` item. These are\n * provider-native input items (`function_call`, `function_call_output`) whose\n * fields differ per item type, so — unlike the Anthropic `{ role, content }`\n * projection — the whole object is preserved verbatim.\n *\n * The static input is already typed `JsonObject`, so the `isJsonObject` guard\n * is a runtime backstop, not a compile-time narrowing: continuation messages\n * originate from a prior turn's `IAiClientToolContinuation.messages` and a\n * consumer may persist and reload them through untyped JSON before passing them\n * back. The guard preserves the same \"a malformed continuation message is\n * better omitted than transmitted verbatim\" posture as the Anthropic path —\n * non-object entries fail conversion and are skipped by the caller.\n * @internal\n */\nconst openAiRawTailItemConverter: Converter<JsonObject> = Converters.isA<JsonObject>(\n 'JsonObject',\n (v): v is JsonObject => isJsonObject(v)\n);\n\n/**\n * Converter for a Gemini `rawTail` item. Gemini continuation messages are\n * `{ role, parts }` turns (a model turn with `functionCall` parts followed by a\n * user turn with `functionResponse` parts). Narrows a `JsonObject` to\n * `{ role: 'user' | 'model'; parts: Array<Record<string, unknown>> }`; entries\n * that fail validation are skipped by the caller.\n * @internal\n */\nconst geminiRawTailMessageConverter: Converter<{\n role: 'user' | 'model';\n parts: unknown[];\n}> = Converters.object<{ role: 'user' | 'model'; parts: unknown[] }>(\n {\n role: Converters.enumeratedValue<'user' | 'model'>(['user', 'model']),\n // `parts` is preserved verbatim and serialized into the request body, so the\n // element shape is not narrowed here — `Array.isArray` soundly guarantees\n // `unknown[]` (narrowing to `Record<string, unknown>[]` would be an unchecked cast).\n parts: Converters.isA('array', (v): v is unknown[] => Array.isArray(v))\n },\n { strict: false }\n);\n\n/**\n * Optional history (`head`) and raw continuation (`rawTail`) messages to weave\n * around the prompt's current user message.\n *\n * @internal\n */\nexport interface IBuildMessagesOptions {\n /**\n * Prior conversation history inserted between the system prompt and the\n * prompt's current user message (multi-turn chat / correction retries). The\n * single ordered linearization is `[system, ...head, user, ...rawTail]`.\n */\n readonly head?: ReadonlyArray<IChatMessage>;\n /**\n * Raw JSON objects appended after the prompt's user message. Used to\n * inject provider-specific continuation messages (e.g. Anthropic assistant\n * turns with thinking blocks, OpenAI Responses `function_call` /\n * `function_call_output` items, Gemini `functionCall` / `functionResponse`\n * turns) that cannot be expressed as plain {@link IChatMessage} objects.\n *\n * Each builder applies its own provider-specific shape guard:\n * - {@link buildAnthropicMessages} projects each entry to `{ role, content }`.\n * - {@link buildMessages} (OpenAI / xAI Responses) preserves each item\n * verbatim (item fields differ per `type`), guarding only that it is a\n * JSON object.\n * - {@link buildGeminiContents} projects each entry to `{ role, parts }`.\n *\n * Entries that fail their builder's shape check are silently skipped (the\n * caller is responsible for supplying well-formed continuation messages).\n * Appended after the current user message.\n */\n readonly rawTail?: ReadonlyArray<JsonObject>;\n}\n\n/**\n * Builds the messages array from prompt + optional history (`head`) and raw\n * continuation (`rawTail`) messages. The caller supplies the system content\n * (string when no cache breakpoints are requested, parts array when they are\n * — see {@link buildOpenAiChatSystemContent} / {@link buildOpenAiResponsesSystemContent})\n * and the user content (string for text-only, parts array for vision prompts),\n * since both shapes differ by format.\n *\n * `rawTail` items (OpenAI / xAI Responses `function_call` /\n * `function_call_output` continuation items) are appended verbatim after the\n * user message — their fields differ per item `type`, so they are preserved\n * rather than projected. The return type is `Array<Record<string, unknown>>`\n * to accommodate both `{ role, content }` messages and these heterogeneous\n * input items.\n *\n * @internal\n */\nexport function buildMessages(\n systemContent: string | unknown[],\n userContent: string | unknown[],\n options?: IBuildMessagesOptions\n): Array<Record<string, unknown>> {\n const messages: Array<Record<string, unknown>> = [{ role: 'system', content: systemContent }];\n if (options?.head) {\n for (const msg of options.head) {\n messages.push({ role: msg.role, content: msg.content });\n }\n }\n messages.push({ role: 'user', content: userContent });\n // OpenAI / xAI Responses continuation items (function_call /\n // function_call_output) are appended verbatim — their field set differs per\n // item type, so the whole object is preserved rather than projected.\n if (options?.rawTail) {\n for (const item of options.rawTail) {\n const converted = openAiRawTailItemConverter.convert(item);\n if (converted.isSuccess()) {\n messages.push(converted.value);\n }\n }\n }\n return messages;\n}\n\n/**\n * Splits `segments` (from {@link splitSystemForCache}) into a single string when nothing carries\n * a breakpoint, or a content-part array in `partType`'s shape (with `prompt_cache_breakpoint` on\n * the parts that end a cacheable prefix) otherwise.\n *\n * @remarks\n * A single non-breakpointed segment (the common case: `cache` omitted or empty) returns the\n * plain string, byte-identical to a request built before cache support existed. Shared by\n * {@link buildOpenAiChatSystemContent} and {@link buildOpenAiResponsesSystemContent}, whose only\n * difference is the content-part `type` OpenAI's two APIs use (`'text'` vs `'input_text'`,\n * research.md §1.2).\n * @internal\n */\nfunction toOpenAiSystemContent(\n segments: ReadonlyArray<{ readonly text: string; readonly cacheBreakpoint: boolean }>,\n partType: 'text' | 'input_text'\n): string | unknown[] {\n if (segments.length === 1 && !segments[0].cacheBreakpoint) {\n return segments[0].text;\n }\n return segments.map((seg) => ({\n type: partType,\n text: seg.text,\n ...(seg.cacheBreakpoint\n ? {\n // eslint-disable-next-line @typescript-eslint/naming-convention -- wire field name\n prompt_cache_breakpoint: { mode: 'explicit' }\n }\n : {})\n }));\n}\n\n/**\n * Builds the system content for OpenAI Chat Completions, splitting `system` at `cache`'s\n * breakpoints (design.md §6.3) into content parts each carrying\n * `prompt_cache_breakpoint: { mode: 'explicit' }` where a cacheable prefix ends.\n * `prompt_cache_options` is never touched — OpenAI keeps its own implicit breakpoint and writes\n * up to the latest three of ours on top of it. Returns the plain `system` string when `cache` is\n * `undefined` or declares no breakpoints, byte-identical to a request built before cache support\n * existed.\n * @internal\n */\nexport function buildOpenAiChatSystemContent(\n system: string,\n cache: IAiCacheRequest | undefined\n): Result<string | unknown[]> {\n return splitSystemForCache(system, cache).onSuccess((segments) =>\n succeed(toOpenAiSystemContent(segments, 'text'))\n );\n}\n\n/**\n * Builds the system content for the OpenAI / xAI Responses API. Identical to\n * {@link buildOpenAiChatSystemContent} except for the content-part type (`'input_text'` instead\n * of `'text'`) — see research.md §1.2, \"Where the breakpoint may be attached.\"\n * @internal\n */\nexport function buildOpenAiResponsesSystemContent(\n system: string,\n cache: IAiCacheRequest | undefined\n): Result<string | unknown[]> {\n return splitSystemForCache(system, cache).onSuccess((segments) =>\n succeed(toOpenAiSystemContent(segments, 'input_text'))\n );\n}\n\n/**\n * Builds the Anthropic `system` field, splitting `system` at `cache`'s breakpoints into content\n * blocks carrying `cache_control: { type: 'ephemeral' }` on the block that ends each cacheable\n * prefix (design.md §6.1). Returns the plain `system` string when `cache` is `undefined` or\n * declares no breakpoints — byte-identical to `system: prompt.system` from a request built\n * before cache support existed.\n * @internal\n */\nexport function buildAnthropicSystem(\n system: string,\n cache: IAiCacheRequest | undefined\n): Result<string | unknown[]> {\n return splitSystemForCache(system, cache).onSuccess((segments) => {\n if (segments.length === 1 && !segments[0].cacheBreakpoint) {\n return succeed<string | unknown[]>(segments[0].text);\n }\n return succeed<string | unknown[]>(\n segments.map((seg) => ({\n type: 'text',\n text: seg.text,\n ...(seg.cacheBreakpoint ? { cache_control: { type: 'ephemeral' } } : {})\n }))\n );\n });\n}\n\n/**\n * Builds the user content for OpenAI Chat Completions when attachments are\n * present. Returns a string when there are no attachments.\n *\n * @internal\n */\nexport function buildOpenAiChatUserContent(prompt: AiPrompt): string | unknown[] {\n if (prompt.attachments.length === 0) {\n return prompt.user;\n }\n return [\n { type: 'text', text: prompt.user },\n ...prompt.attachments.map((att: IAiImageAttachment) => ({\n type: 'image_url',\n image_url: {\n url: toDataUrl(att),\n ...(att.detail !== undefined ? { detail: att.detail } : {})\n }\n }))\n ];\n}\n\n/**\n * Builds the user content for OpenAI / xAI Responses API when attachments\n * are present. Responses API uses `input_text` / `input_image` part types,\n * distinct from Chat Completions' `text` / `image_url`.\n *\n * @internal\n */\nexport function buildOpenAiResponsesUserContent(prompt: AiPrompt): string | unknown[] {\n if (prompt.attachments.length === 0) {\n return prompt.user;\n }\n return [\n { type: 'input_text', text: prompt.user },\n ...prompt.attachments.map((att: IAiImageAttachment) => ({\n type: 'input_image',\n image_url: toDataUrl(att),\n ...(att.detail !== undefined ? { detail: att.detail } : {})\n }))\n ];\n}\n\n/**\n * Builds the user-message content for Anthropic when attachments are present.\n *\n * @internal\n */\nexport function buildAnthropicUserContent(prompt: AiPrompt): string | unknown[] {\n if (prompt.attachments.length === 0) {\n return prompt.user;\n }\n return [\n { type: 'text', text: prompt.user },\n ...prompt.attachments.map((att: IAiImageAttachment) => ({\n type: 'image',\n source: {\n type: 'base64',\n media_type: att.mimeType,\n data: att.base64\n }\n }))\n ];\n}\n\n/**\n * Builds the Gemini `parts` array for the user turn, including any image\n * attachments as `inlineData` parts.\n *\n * @internal\n */\nexport function buildGeminiUserParts(prompt: AiPrompt): Array<Record<string, unknown>> {\n const parts: Array<Record<string, unknown>> = [{ text: prompt.user }];\n for (const att of prompt.attachments) {\n parts.push({ inlineData: { mimeType: att.mimeType, data: att.base64 } });\n }\n return parts;\n}\n\n/**\n * Builds the Anthropic messages array, weaving any `head` history messages\n * between implicit system + the prompt's user message and appending `rawTail`\n * continuation messages after. System messages are filtered out (Anthropic uses\n * a top-level system field).\n *\n * @internal\n */\nexport function buildAnthropicMessages(\n prompt: AiPrompt,\n options?: IBuildMessagesOptions\n): Array<{ role: string; content: string | unknown[] }> {\n const messages: Array<{ role: string; content: string | unknown[] }> = [];\n if (options?.head) {\n for (const msg of options.head) {\n if (msg.role !== 'system') {\n messages.push({ role: msg.role, content: msg.content });\n }\n }\n }\n messages.push({ role: 'user', content: buildAnthropicUserContent(prompt) });\n if (options?.rawTail) {\n for (const msg of options.rawTail) {\n const converted = rawTailMessageConverter.convert(msg);\n if (converted.isSuccess()) {\n messages.push(converted.value);\n }\n }\n }\n return messages;\n}\n\n/**\n * Builds the Gemini `contents` array, weaving any `head` history messages before\n * the prompt's user parts and appending `rawTail` continuation messages after.\n * System messages are filtered out (Gemini uses a top-level systemInstruction\n * field) and assistant roles are mapped to Gemini's `model` role.\n *\n * @internal\n */\nexport function buildGeminiContents(\n prompt: AiPrompt,\n options?: IBuildMessagesOptions\n): Array<{ role: string; parts: unknown[] }> {\n const contents: Array<{ role: string; parts: unknown[] }> = [];\n if (options?.head) {\n for (const msg of options.head) {\n if (msg.role !== 'system') {\n contents.push({\n role: msg.role === 'assistant' ? 'model' : msg.role,\n parts: [{ text: msg.content }]\n });\n }\n }\n }\n contents.push({ role: 'user', parts: buildGeminiUserParts(prompt) });\n // Gemini continuation turns (model `functionCall` parts + user\n // `functionResponse` parts) are projected to `{ role, parts }`.\n if (options?.rawTail) {\n for (const item of options.rawTail) {\n const converted = geminiRawTailMessageConverter.convert(item);\n if (converted.isSuccess()) {\n contents.push(converted.value);\n }\n }\n }\n return contents;\n}\n"]}
|
|
@@ -85,6 +85,19 @@ export declare function callProviderCompletion(params: IProviderCompletionParams
|
|
|
85
85
|
* CORS, and API key forwarding. The request body serializes the unified
|
|
86
86
|
* {@link AiAssist.IChatRequest} shape (`system?` + `messages`). Enforces the same
|
|
87
87
|
* non-empty / trailing-user-turn and image-input invariants as the direct path.
|
|
88
|
+
*
|
|
89
|
+
* @remarks
|
|
90
|
+
* Two parameters are handled before the body is composed, rather than forwarded.
|
|
91
|
+
* `tier` is resolved here and sent as a concrete `modelOverride`, because both
|
|
92
|
+
* halves of the resolution live on the descriptor the caller already holds — so a
|
|
93
|
+
* proxy needs no `tier` vocabulary, where a `tier` body field it did not understand
|
|
94
|
+
* would be ignored and silently serve a frontier request from the base model.
|
|
95
|
+
* `endpoint` is **refused**: it names where the prompt must go, the proxy is what
|
|
96
|
+
* makes that call, and the field has never been sent to one — so honoring it is
|
|
97
|
+
* unverifiable and ignoring it would reach the provider's default upstream instead
|
|
98
|
+
* of the host the caller pinned. Use `callProviderCompletion`, or point the
|
|
99
|
+
* proxy itself at the intended upstream.
|
|
100
|
+
*
|
|
88
101
|
* @param proxyUrl - Base URL of the proxy server
|
|
89
102
|
* @param params - Same parameters as {@link callProviderCompletion}
|
|
90
103
|
* @public
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"completionClient.d.ts","sourceRoot":"","sources":["../../../src/packlets/ai-assist/completionClient.ts"],"names":[],"mappings":"AA8BA,OAAO,EAGL,KAAK,OAAO,EACZ,MAAM,EAIP,MAAM,eAAe,CAAC;AAEvB,OAAO,EAEL,KAAK,kBAAkB,EAEvB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAE1B,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,SAAS,EAMf,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"completionClient.d.ts","sourceRoot":"","sources":["../../../src/packlets/ai-assist/completionClient.ts"],"names":[],"mappings":"AA8BA,OAAO,EAGL,KAAK,OAAO,EACZ,MAAM,EAIP,MAAM,eAAe,CAAC;AAEvB,OAAO,EAEL,KAAK,kBAAkB,EAEvB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAE1B,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,SAAS,EAMf,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AA2CtD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAYvE;;;;;GAKG;AACH,MAAM,WAAW,yBAA0B,SAAQ,YAAY;IAC7D,8BAA8B;IAC9B,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC;IAC3C,iCAAiC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,qGAAqG;IACrG,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,CAAC;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;IACxC,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC;IAClC,uGAAuG;IACvG,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IACnD,kEAAkE;IAClE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC;IACpC;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,uBAAuB,CAAC;IACpD;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAC;CAClC;AA0lBD;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,CAwMxC;AAMD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,CAwLxC"}
|