@providerprotocol/ai 0.0.38 → 0.0.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +363 -37
- package/dist/anthropic/index.d.ts +3 -2
- package/dist/anthropic/index.js +7 -5
- package/dist/anthropic/index.js.map +1 -1
- package/dist/cerebras/index.d.ts +3 -2
- package/dist/cerebras/index.js +7 -5
- package/dist/cerebras/index.js.map +1 -1
- package/dist/chunk-3Q5VELKG.js +124 -0
- package/dist/chunk-3Q5VELKG.js.map +1 -0
- package/dist/{chunk-WU4U6IHF.js → chunk-6QCV4WXF.js} +4 -13
- package/dist/chunk-6QCV4WXF.js.map +1 -0
- package/dist/{chunk-LTEMH3CI.js → chunk-AC3VHSZJ.js} +6 -4
- package/dist/{chunk-LTEMH3CI.js.map → chunk-AC3VHSZJ.js.map} +1 -1
- package/dist/{chunk-YQLR3XOA.js → chunk-BIBMNP7Y.js} +1 -75
- package/dist/chunk-BIBMNP7Y.js.map +1 -0
- package/dist/{chunk-CRP6Y7NF.js → chunk-CWGTARDE.js} +2 -2
- package/dist/{chunk-ZRVNAET3.js → chunk-DI47UY2H.js} +6 -3
- package/dist/chunk-DI47UY2H.js.map +1 -0
- package/dist/{chunk-7GTWHZY2.js → chunk-EHR3LIPS.js} +5 -3
- package/dist/{chunk-7GTWHZY2.js.map → chunk-EHR3LIPS.js.map} +1 -1
- package/dist/chunk-EY2LLDGY.js +94 -0
- package/dist/chunk-EY2LLDGY.js.map +1 -0
- package/dist/{chunk-MJI74VEJ.js → chunk-F5ENANMJ.js} +18 -2
- package/dist/chunk-F5ENANMJ.js.map +1 -0
- package/dist/chunk-IKJH5ZSJ.js +1 -0
- package/dist/chunk-IKJH5ZSJ.js.map +1 -0
- package/dist/{chunk-4RX4VQCB.js → chunk-KBI45OXI.js} +2 -2
- package/dist/{chunk-5IWHCXKN.js → chunk-KVUOTFYZ.js} +2 -2
- package/dist/{chunk-EPB3GQNL.js → chunk-L6QWKFGE.js} +13 -3
- package/dist/chunk-L6QWKFGE.js.map +1 -0
- package/dist/{chunk-BDXH6NQS.js → chunk-N4LAFGLX.js} +7 -7
- package/dist/{chunk-ZKNPQBIE.js → chunk-R3T2IYOU.js} +5 -3
- package/dist/{chunk-ZKNPQBIE.js.map → chunk-R3T2IYOU.js.map} +1 -1
- package/dist/chunk-U2G5PHHL.js +25 -0
- package/dist/chunk-U2G5PHHL.js.map +1 -0
- package/dist/{chunk-SBGZJVTJ.js → chunk-VQZPADW6.js} +100 -33
- package/dist/chunk-VQZPADW6.js.map +1 -0
- package/dist/{chunk-FYSZFIZS.js → chunk-XTWBAL42.js} +5 -3
- package/dist/{chunk-FYSZFIZS.js.map → chunk-XTWBAL42.js.map} +1 -1
- package/dist/{chunk-2YXFLRQ6.js → chunk-ZMESKGUY.js} +2 -2
- package/dist/chunk-ZSZVWLGE.js +83 -0
- package/dist/chunk-ZSZVWLGE.js.map +1 -0
- package/dist/{embedding-CwZ1ZNWv.d.ts → embedding-ts1npsDg.d.ts} +1 -1
- package/dist/google/index.d.ts +38 -3
- package/dist/google/index.js +5 -4
- package/dist/google/index.js.map +1 -1
- package/dist/groq/index.d.ts +3 -2
- package/dist/groq/index.js +7 -5
- package/dist/groq/index.js.map +1 -1
- package/dist/http/index.d.ts +5 -4
- package/dist/http/index.js +19 -22
- package/dist/{image-stream-CeQHtjxS.d.ts → image-stream-BPml2YZZ.d.ts} +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.js +316 -113
- package/dist/index.js.map +1 -1
- package/dist/{llm-DS_-l71X.d.ts → llm-BWLaTzzY.d.ts} +89 -36
- package/dist/middleware/logging/index.d.ts +3 -2
- package/dist/middleware/logging/index.js +3 -0
- package/dist/middleware/logging/index.js.map +1 -1
- package/dist/middleware/parsed-object/index.d.ts +3 -2
- package/dist/middleware/parsed-object/index.js +5 -1
- package/dist/middleware/parsed-object/index.js.map +1 -1
- package/dist/middleware/persistence/index.d.ts +3 -2
- package/dist/middleware/persistence/index.js +3 -2
- package/dist/middleware/persistence/index.js.map +1 -1
- package/dist/middleware/pipeline/index.d.ts +195 -0
- package/dist/middleware/pipeline/index.js +61 -0
- package/dist/middleware/pipeline/index.js.map +1 -0
- package/dist/middleware/pubsub/index.d.ts +13 -10
- package/dist/middleware/pubsub/index.js +78 -6
- package/dist/middleware/pubsub/index.js.map +1 -1
- package/dist/middleware/pubsub/server/express/index.d.ts +3 -2
- package/dist/middleware/pubsub/server/express/index.js +2 -2
- package/dist/middleware/pubsub/server/fastify/index.d.ts +3 -2
- package/dist/middleware/pubsub/server/fastify/index.js +2 -2
- package/dist/middleware/pubsub/server/h3/index.d.ts +3 -2
- package/dist/middleware/pubsub/server/h3/index.js +2 -2
- package/dist/middleware/pubsub/server/index.d.ts +50 -8
- package/dist/middleware/pubsub/server/index.js +5 -5
- package/dist/middleware/pubsub/server/index.js.map +1 -1
- package/dist/middleware/pubsub/server/webapi/index.d.ts +3 -2
- package/dist/middleware/pubsub/server/webapi/index.js +2 -2
- package/dist/moonshot/index.d.ts +3 -2
- package/dist/moonshot/index.js +7 -5
- package/dist/moonshot/index.js.map +1 -1
- package/dist/ollama/index.d.ts +24 -3
- package/dist/ollama/index.js +5 -4
- package/dist/ollama/index.js.map +1 -1
- package/dist/openai/index.d.ts +65 -3
- package/dist/openai/index.js +7 -5
- package/dist/openai/index.js.map +1 -1
- package/dist/openrouter/index.d.ts +4 -3
- package/dist/openrouter/index.js +7 -5
- package/dist/openrouter/index.js.map +1 -1
- package/dist/proxy/index.d.ts +5 -4
- package/dist/proxy/index.js +20 -17
- package/dist/proxy/index.js.map +1 -1
- package/dist/proxy/server/express/index.d.ts +8 -8
- package/dist/proxy/server/express/index.js +5 -3
- package/dist/proxy/server/fastify/index.d.ts +8 -8
- package/dist/proxy/server/fastify/index.js +5 -3
- package/dist/proxy/server/h3/index.d.ts +22 -21
- package/dist/proxy/server/h3/index.js +5 -3
- package/dist/proxy/server/index.d.ts +5 -4
- package/dist/proxy/server/index.js +15 -13
- package/dist/proxy/server/webapi/index.d.ts +8 -8
- package/dist/proxy/server/webapi/index.js +5 -3
- package/dist/responses/index.d.ts +3 -2
- package/dist/responses/index.js +7 -5
- package/dist/responses/index.js.map +1 -1
- package/dist/retry-DVfdPLIB.d.ts +322 -0
- package/dist/{stream-sXhBtWjl.d.ts → stream-bBd_4Ipu.d.ts} +29 -419
- package/dist/tool-BmAfKNBq.d.ts +507 -0
- package/dist/{types-Cr4F0tVy.d.ts → types-nTwlpyJE.d.ts} +28 -3
- package/dist/utils/index.d.ts +129 -1
- package/dist/utils/index.js +28 -1
- package/dist/xai/index.d.ts +3 -2
- package/dist/xai/index.js +7 -5
- package/dist/xai/index.js.map +1 -1
- package/package.json +20 -3
- package/dist/chunk-ARVM24K2.js +0 -128
- package/dist/chunk-ARVM24K2.js.map +0 -1
- package/dist/chunk-EPB3GQNL.js.map +0 -1
- package/dist/chunk-MJI74VEJ.js.map +0 -1
- package/dist/chunk-SBGZJVTJ.js.map +0 -1
- package/dist/chunk-WU4U6IHF.js.map +0 -1
- package/dist/chunk-Y5H7C5J4.js +0 -263
- package/dist/chunk-Y5H7C5J4.js.map +0 -1
- package/dist/chunk-YQLR3XOA.js.map +0 -1
- package/dist/chunk-ZRVNAET3.js.map +0 -1
- package/dist/retry-CgoBNa51.d.ts +0 -531
- /package/dist/{chunk-CRP6Y7NF.js.map → chunk-CWGTARDE.js.map} +0 -0
- /package/dist/{chunk-4RX4VQCB.js.map → chunk-KBI45OXI.js.map} +0 -0
- /package/dist/{chunk-5IWHCXKN.js.map → chunk-KVUOTFYZ.js.map} +0 -0
- /package/dist/{chunk-BDXH6NQS.js.map → chunk-N4LAFGLX.js.map} +0 -0
- /package/dist/{chunk-2YXFLRQ6.js.map → chunk-ZMESKGUY.js.map} +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { I as ImageSource, c as ImageBlock,
|
|
1
|
+
import { I as ImageSource, c as ImageBlock, S as StreamEvent, T as Turn, M as Message, U as UserContent, A as AssistantContent, d as MessageType, a as MessageJSON, e as AssistantMessage, f as TokenUsage, C as ContentBlock, g as StreamResult } from './stream-bBd_4Ipu.js';
|
|
2
|
+
import { e as Tool, d as ToolInput, f as ToolUseStrategy, S as Structure, J as JSONSchema } from './tool-BmAfKNBq.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* @fileoverview Error types for the Unified Provider Protocol.
|
|
@@ -392,6 +393,14 @@ interface MiddlewareContext {
|
|
|
392
393
|
readonly startTime: number;
|
|
393
394
|
/** Request end timestamp in milliseconds (set after completion) */
|
|
394
395
|
endTime?: number;
|
|
396
|
+
/**
|
|
397
|
+
* Emit a stream event. Events flow through onStreamEvent for all middleware.
|
|
398
|
+
* Useful for emitting events after streaming completes (e.g., in onTurn hooks).
|
|
399
|
+
* No-op for non-streaming requests.
|
|
400
|
+
*
|
|
401
|
+
* @param event - The stream event to emit
|
|
402
|
+
*/
|
|
403
|
+
emit(event: StreamEvent): void;
|
|
395
404
|
}
|
|
396
405
|
/**
|
|
397
406
|
* Context for stream event hooks.
|
|
@@ -483,6 +492,16 @@ interface Middleware {
|
|
|
483
492
|
* @param ctx - The middleware context
|
|
484
493
|
*/
|
|
485
494
|
onAbort?(error: Error, ctx: MiddlewareContext): void | Promise<void>;
|
|
495
|
+
/**
|
|
496
|
+
* Called before a retry attempt is made after a retryable error.
|
|
497
|
+
* Allows middleware to reset state or perform cleanup before the retry.
|
|
498
|
+
* Called for all middleware that have this hook, in forward order.
|
|
499
|
+
*
|
|
500
|
+
* @param attempt - The retry attempt number (1-indexed)
|
|
501
|
+
* @param error - The error that triggered the retry
|
|
502
|
+
* @param ctx - The middleware context
|
|
503
|
+
*/
|
|
504
|
+
onRetry?(attempt: number, error: Error, ctx: MiddlewareContext): void | Promise<void>;
|
|
486
505
|
/**
|
|
487
506
|
* Called before provider execution. Can modify the request.
|
|
488
507
|
*
|
|
@@ -850,20 +869,15 @@ interface BoundImageModel<TParams = unknown> {
|
|
|
850
869
|
*
|
|
851
870
|
* @example
|
|
852
871
|
* ```typescript
|
|
853
|
-
*
|
|
854
|
-
*
|
|
855
|
-
* private index = 0;
|
|
856
|
-
*
|
|
857
|
-
* constructor(keys: string[]) {
|
|
858
|
-
* this.keys = keys;
|
|
859
|
-
* }
|
|
872
|
+
* // Built-in key strategies
|
|
873
|
+
* const keys = roundRobinKeys(['sk-key-1', 'sk-key-2']);
|
|
860
874
|
*
|
|
875
|
+
* // Custom implementation
|
|
876
|
+
* const custom: KeyStrategy = {
|
|
861
877
|
* getKey(): string {
|
|
862
|
-
*
|
|
863
|
-
* this.index = (this.index + 1) % this.keys.length;
|
|
864
|
-
* return key;
|
|
878
|
+
* return pickKeyFromPool();
|
|
865
879
|
* }
|
|
866
|
-
* }
|
|
880
|
+
* };
|
|
867
881
|
* ```
|
|
868
882
|
*/
|
|
869
883
|
interface KeyStrategy {
|
|
@@ -877,24 +891,32 @@ interface KeyStrategy {
|
|
|
877
891
|
/**
|
|
878
892
|
* Retry strategy interface for handling request failures.
|
|
879
893
|
*
|
|
880
|
-
*
|
|
881
|
-
*
|
|
894
|
+
* Each request receives a fresh strategy instance via the factory pattern,
|
|
895
|
+
* ensuring complete isolation between concurrent requests.
|
|
882
896
|
*
|
|
883
897
|
* @example
|
|
884
898
|
* ```typescript
|
|
885
|
-
*
|
|
886
|
-
*
|
|
887
|
-
*
|
|
888
|
-
*
|
|
889
|
-
*
|
|
890
|
-
*
|
|
899
|
+
* // Using built-in strategies
|
|
900
|
+
* const provider = createOpenAI({
|
|
901
|
+
* retryStrategy: exponentialBackoff({ maxAttempts: 5 })
|
|
902
|
+
* });
|
|
903
|
+
*
|
|
904
|
+
* // Custom strategy factory
|
|
905
|
+
* const customRetry = (): RetryStrategy => ({
|
|
906
|
+
* onRetry(error, attempt) {
|
|
907
|
+
* if (attempt > 3) return null;
|
|
891
908
|
* if (error.code !== 'RATE_LIMITED') return null;
|
|
892
|
-
* return
|
|
909
|
+
* return 1000 * Math.pow(2, attempt - 1);
|
|
893
910
|
* }
|
|
894
|
-
* }
|
|
911
|
+
* });
|
|
895
912
|
* ```
|
|
896
913
|
*/
|
|
897
914
|
interface RetryStrategy {
|
|
915
|
+
/**
|
|
916
|
+
* Maximum number of retry attempts configured for this strategy.
|
|
917
|
+
* Used by streaming retry logic to emit progress events.
|
|
918
|
+
*/
|
|
919
|
+
readonly maxAttempts?: number;
|
|
898
920
|
/**
|
|
899
921
|
* Called when a request fails with a retryable error.
|
|
900
922
|
*
|
|
@@ -904,7 +926,7 @@ interface RetryStrategy {
|
|
|
904
926
|
*/
|
|
905
927
|
onRetry(error: UPPError, attempt: number): number | null | Promise<number | null>;
|
|
906
928
|
/**
|
|
907
|
-
* Called before each request. Can be used to implement pre-emptive
|
|
929
|
+
* Called before each request. Can be used to implement pre-emptive delays.
|
|
908
930
|
*
|
|
909
931
|
* @returns Delay in ms to wait before making the request, or 0 to proceed immediately
|
|
910
932
|
*/
|
|
@@ -913,7 +935,32 @@ interface RetryStrategy {
|
|
|
913
935
|
* Reset the strategy state (e.g., after a successful request).
|
|
914
936
|
*/
|
|
915
937
|
reset?(): void;
|
|
938
|
+
/**
|
|
939
|
+
* Sets the retry delay from a Retry-After header value.
|
|
940
|
+
* Only applicable to strategies that honor server-provided retry timing.
|
|
941
|
+
*
|
|
942
|
+
* @param seconds - The Retry-After value in seconds
|
|
943
|
+
*/
|
|
944
|
+
setRetryAfter?(seconds: number): void;
|
|
916
945
|
}
|
|
946
|
+
/**
|
|
947
|
+
* Factory function that creates a fresh RetryStrategy instance per request.
|
|
948
|
+
*
|
|
949
|
+
* Using a factory ensures each `.stream()`, `.generate()`, `.embed()` call
|
|
950
|
+
* gets its own isolated retry state, preventing cross-request contamination.
|
|
951
|
+
*
|
|
952
|
+
* @example
|
|
953
|
+
* ```typescript
|
|
954
|
+
* // Built-in factory
|
|
955
|
+
* const retry = exponentialBackoff({ maxAttempts: 3 });
|
|
956
|
+
*
|
|
957
|
+
* // Custom factory
|
|
958
|
+
* const customRetry: RetryStrategyFactory = () => ({
|
|
959
|
+
* onRetry: (error, attempt) => attempt <= 3 ? 1000 : null
|
|
960
|
+
* });
|
|
961
|
+
* ```
|
|
962
|
+
*/
|
|
963
|
+
type RetryStrategyFactory = () => RetryStrategy;
|
|
917
964
|
/**
|
|
918
965
|
* Provider identity shape for structural typing.
|
|
919
966
|
*
|
|
@@ -937,12 +984,12 @@ interface ProviderIdentity {
|
|
|
937
984
|
* const config: ProviderConfig = {
|
|
938
985
|
* apiKey: process.env.OPENAI_API_KEY,
|
|
939
986
|
* timeout: 30000,
|
|
940
|
-
* retryStrategy:
|
|
987
|
+
* retryStrategy: exponentialBackoff()
|
|
941
988
|
* };
|
|
942
989
|
*
|
|
943
990
|
* // Or with a key strategy for key rotation
|
|
944
991
|
* const config: ProviderConfig = {
|
|
945
|
-
* apiKey:
|
|
992
|
+
* apiKey: roundRobinKeys(['sk-1', 'sk-2', 'sk-3']),
|
|
946
993
|
* baseUrl: 'https://custom-proxy.example.com'
|
|
947
994
|
* };
|
|
948
995
|
* ```
|
|
@@ -964,8 +1011,8 @@ interface ProviderConfig {
|
|
|
964
1011
|
fetch?: typeof fetch;
|
|
965
1012
|
/** API version override (provider-specific) */
|
|
966
1013
|
apiVersion?: string;
|
|
967
|
-
/** Retry strategy for handling failures and rate limits */
|
|
968
|
-
retryStrategy?:
|
|
1014
|
+
/** Retry strategy factory for handling failures and rate limits */
|
|
1015
|
+
retryStrategy?: RetryStrategyFactory;
|
|
969
1016
|
/**
|
|
970
1017
|
* Custom headers to include in API requests.
|
|
971
1018
|
*
|
|
@@ -1590,12 +1637,12 @@ interface LLMOptions<TParams = unknown> {
|
|
|
1590
1637
|
* Array formats are passed through directly to the provider.
|
|
1591
1638
|
*/
|
|
1592
1639
|
system?: string | unknown[];
|
|
1593
|
-
/** Tools available to the model */
|
|
1594
|
-
tools?:
|
|
1640
|
+
/** Tools available to the model (accepts Zod schemas for parameters) */
|
|
1641
|
+
tools?: ToolInput[];
|
|
1595
1642
|
/** Tool execution strategy */
|
|
1596
1643
|
toolStrategy?: ToolUseStrategy;
|
|
1597
|
-
/** Structured output schema (JSON Schema) */
|
|
1598
|
-
structure?:
|
|
1644
|
+
/** Structured output schema (JSON Schema or Zod schema) */
|
|
1645
|
+
structure?: Structure;
|
|
1599
1646
|
/**
|
|
1600
1647
|
* Middleware for intercepting and transforming requests, responses, and streams.
|
|
1601
1648
|
*
|
|
@@ -1648,27 +1695,33 @@ interface LLMInstance<TParams = unknown> {
|
|
|
1648
1695
|
* Executes inference and returns the complete Turn.
|
|
1649
1696
|
*
|
|
1650
1697
|
* Supports multiple calling patterns:
|
|
1698
|
+
* - No input (system-only): `generate()`
|
|
1651
1699
|
* - Single input: `generate('Hello')`
|
|
1652
1700
|
* - Multiple inputs: `generate('Context...', 'Question?')`
|
|
1653
1701
|
* - With history: `generate(messages, 'Follow-up?')`
|
|
1654
1702
|
* - With thread: `generate(thread, 'Next message')`
|
|
1655
1703
|
*
|
|
1656
|
-
* @param historyOrInput -
|
|
1704
|
+
* @param historyOrInput - Optional history (Message[] or Thread) or first input
|
|
1657
1705
|
* @param input - Additional inputs to include in the request
|
|
1658
1706
|
* @returns Promise resolving to the complete Turn
|
|
1659
1707
|
*/
|
|
1660
|
-
generate(historyOrInput
|
|
1708
|
+
generate(historyOrInput?: Message[] | Thread | InferenceInput, ...input: InferenceInput[]): Promise<Turn>;
|
|
1661
1709
|
/**
|
|
1662
1710
|
* Executes streaming inference.
|
|
1663
1711
|
*
|
|
1664
1712
|
* Returns an async iterable of stream events that can also
|
|
1665
1713
|
* be awaited for the final Turn.
|
|
1666
1714
|
*
|
|
1667
|
-
*
|
|
1715
|
+
* Supports multiple calling patterns:
|
|
1716
|
+
* - No input (system-only): `stream()`
|
|
1717
|
+
* - Single input: `stream('Hello')`
|
|
1718
|
+
* - With history: `stream(messages, 'Follow-up?')`
|
|
1719
|
+
*
|
|
1720
|
+
* @param historyOrInput - Optional history (Message[] or Thread) or first input
|
|
1668
1721
|
* @param input - Additional inputs to include in the request
|
|
1669
1722
|
* @returns StreamResult that yields events and resolves to Turn
|
|
1670
1723
|
*/
|
|
1671
|
-
stream(historyOrInput
|
|
1724
|
+
stream(historyOrInput?: Message[] | Thread | InferenceInput, ...input: InferenceInput[]): StreamResult;
|
|
1672
1725
|
/** The bound model instance */
|
|
1673
1726
|
readonly model: BoundLLMModel<TParams>;
|
|
1674
1727
|
/** Current system prompt (string or provider-specific array format) */
|
|
@@ -1770,4 +1823,4 @@ interface BoundLLMModel<TParams = unknown> {
|
|
|
1770
1823
|
stream(request: LLMRequest<TParams>): LLMStreamResult;
|
|
1771
1824
|
}
|
|
1772
1825
|
|
|
1773
|
-
export { type
|
|
1826
|
+
export { type StreamContext as $, type LLMStreamResult as A, type BoundEmbeddingModel as B, type BoundLLMModel as C, type InferenceInput as D, type EmbeddingInput as E, type ImageInput as F, type ImageEditInput as G, type ImageGenerateOptions as H, type ImageStreamResult as I, type GeneratedImage as J, type KeyStrategy as K, type LLMOptions as L, type Middleware as M, type ImageUsage as N, type ImageResult as O, type ProviderIdentity as P, type ImageStreamEvent as Q, type RetryStrategyFactory as R, type ImageCapabilities as S, Thread as T, UPPError as U, type ImageRequest as V, type ImageEditRequest as W, type ImageResponse as X, type BoundImageModel as Y, type ImageModelInput as Z, type MiddlewareContext as _, type ThreadJSON as a, type MiddlewareModality as a0, type AnyRequest as a1, type AnyResponse as a2, type ImageProviderStreamResult as b, type ProviderConfig as c, type EmbeddingUsage as d, type Provider as e, type Modality as f, ErrorCode as g, type ModelReference as h, type LLMInstance as i, type ImageOptions as j, type ImageInstance as k, type LLMHandler as l, type EmbeddingHandler as m, type ImageHandler as n, Image as o, ModalityType as p, type RetryStrategy as q, type LLMProvider as r, type EmbeddingProvider as s, type ImageProvider as t, type EmbeddingRequest as u, type EmbeddingResponse as v, type EmbeddingVector as w, type LLMCapabilities as x, type LLMRequest as y, type LLMResponse as z };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { M as Middleware } from '../../llm-
|
|
2
|
-
import '../../stream-
|
|
1
|
+
import { M as Middleware } from '../../llm-BWLaTzzY.js';
|
|
2
|
+
import '../../stream-bBd_4Ipu.js';
|
|
3
|
+
import '../../tool-BmAfKNBq.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* @fileoverview Logging middleware for request/response visibility.
|
|
@@ -49,6 +49,9 @@ function loggingMiddleware(options = {}) {
|
|
|
49
49
|
const duration = Date.now() - ctx.startTime;
|
|
50
50
|
log("warn", `[${ctx.provider}] Aborted after ${duration}ms: ${error.message}`);
|
|
51
51
|
},
|
|
52
|
+
onRetry(attempt, error, ctx) {
|
|
53
|
+
log("warn", `[${ctx.provider}] Retry attempt ${attempt}: ${error.message}`);
|
|
54
|
+
},
|
|
52
55
|
onStreamEvent(event, ctx) {
|
|
53
56
|
if (logStreamEvents) {
|
|
54
57
|
log("debug", `Stream event: ${event.type}`, { index: event.index });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/middleware/logging.ts"],"sourcesContent":["/**\n * @fileoverview Logging middleware for request/response visibility.\n *\n * Provides configurable logging for LLM, embedding, and image operations,\n * including timing, error tracking, and optional event logging.\n *\n * @module middleware/logging\n */\n\nimport type { Middleware, MiddlewareContext, StreamContext } from '../types/middleware.ts';\nimport type { StreamEvent } from '../types/stream.ts';\n\n/**\n * Log levels for filtering output.\n */\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\n/**\n * Options for logging middleware.\n */\nexport interface LoggingOptions {\n /**\n * Minimum log level to output.\n * @default 'info'\n */\n level?: LogLevel;\n\n /**\n * Log individual stream events.\n * @default false\n */\n logStreamEvents?: boolean;\n\n /**\n * Log tool calls and results.\n * @default true\n */\n logToolCalls?: boolean;\n\n /**\n * Custom logger function. If not provided, uses console.log.\n * @param level - The log level\n * @param message - The log message\n * @param data - Optional additional data\n */\n logger?(level: LogLevel, message: string, data?: Record<string, unknown>): void;\n\n /**\n * Prefix for all log messages.\n * @default '[PP]'\n */\n prefix?: string;\n}\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\n/**\n * Creates a logging middleware for visibility into request lifecycle.\n *\n * This middleware logs the start, end, and errors of requests,\n * with optional logging of stream events and tool calls.\n *\n * @param options - Configuration options\n * @returns A middleware that logs request lifecycle events\n *\n * @example\n * ```typescript\n * import { llm } from '@providerprotocol/ai';\n * import { loggingMiddleware } from '@providerprotocol/ai/middleware/logging';\n * import { anthropic } from '@providerprotocol/ai/anthropic';\n *\n * const model = llm({\n * model: anthropic('claude-sonnet-4-20250514'),\n * middleware: [loggingMiddleware({ level: 'debug' })],\n * });\n *\n * // Logs: [PP] [anthropic] Starting llm request (streaming)\n * // Logs: [PP] [anthropic] Completed in 1234ms\n * const result = await model.generate('Hello');\n * ```\n */\nexport function loggingMiddleware(options: LoggingOptions = {}): Middleware {\n const {\n level = 'info',\n logStreamEvents = false,\n logToolCalls = true,\n logger,\n prefix = '[PP]',\n } = options;\n\n const minLevel = LOG_LEVELS[level];\n\n const log = (logLevel: LogLevel, message: string, data?: Record<string, unknown>) => {\n if (LOG_LEVELS[logLevel] < minLevel) {\n return;\n }\n\n const fullMessage = `${prefix} ${message}`;\n\n if (logger) {\n logger(logLevel, fullMessage, data);\n } else {\n const consoleMethod = logLevel === 'error' ? console.error : logLevel === 'warn' ? console.warn : console.log;\n if (data) {\n consoleMethod(fullMessage, data);\n } else {\n consoleMethod(fullMessage);\n }\n }\n };\n\n return {\n name: 'logging',\n\n onStart(ctx: MiddlewareContext): void {\n const streamingLabel = ctx.streaming ? '(streaming)' : '';\n log('info', `[${ctx.provider}] Starting ${ctx.modality} request ${streamingLabel}`.trim());\n log('debug', `[${ctx.provider}] Model: ${ctx.modelId}`);\n },\n\n onEnd(ctx: MiddlewareContext): void {\n const duration = ctx.endTime ? ctx.endTime - ctx.startTime : 0;\n log('info', `[${ctx.provider}] Completed in ${duration}ms`);\n },\n\n onError(error: Error, ctx: MiddlewareContext): void {\n const duration = Date.now() - ctx.startTime;\n log('error', `[${ctx.provider}] Error after ${duration}ms: ${error.message}`);\n },\n\n onAbort(error: Error, ctx: MiddlewareContext): void {\n const duration = Date.now() - ctx.startTime;\n log('warn', `[${ctx.provider}] Aborted after ${duration}ms: ${error.message}`);\n },\n\n onStreamEvent(event: StreamEvent, ctx: StreamContext): StreamEvent {\n if (logStreamEvents) {\n log('debug', `Stream event: ${event.type}`, { index: event.index });\n }\n return event;\n },\n\n onToolCall(tool, params, ctx: MiddlewareContext): void {\n if (logToolCalls) {\n log('info', `[${ctx.provider}] Tool call: ${tool.name}`);\n log('debug', `[${ctx.provider}] Tool params:`, { params });\n }\n },\n\n onToolResult(tool, result, ctx: MiddlewareContext): void {\n if (logToolCalls) {\n log('debug', `[${ctx.provider}] Tool result: ${tool.name}`, { result });\n }\n },\n };\n}\n"],"mappings":";AAsDA,IAAM,aAAuC;AAAA,EAC3C,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AA2BO,SAAS,kBAAkB,UAA0B,CAAC,GAAe;AAC1E,QAAM;AAAA,IACJ,QAAQ;AAAA,IACR,kBAAkB;AAAA,IAClB,eAAe;AAAA,IACf;AAAA,IACA,SAAS;AAAA,EACX,IAAI;AAEJ,QAAM,WAAW,WAAW,KAAK;AAEjC,QAAM,MAAM,CAAC,UAAoB,SAAiB,SAAmC;AACnF,QAAI,WAAW,QAAQ,IAAI,UAAU;AACnC;AAAA,IACF;AAEA,UAAM,cAAc,GAAG,MAAM,IAAI,OAAO;AAExC,QAAI,QAAQ;AACV,aAAO,UAAU,aAAa,IAAI;AAAA,IACpC,OAAO;AACL,YAAM,gBAAgB,aAAa,UAAU,QAAQ,QAAQ,aAAa,SAAS,QAAQ,OAAO,QAAQ;AAC1G,UAAI,MAAM;AACR,sBAAc,aAAa,IAAI;AAAA,MACjC,OAAO;AACL,sBAAc,WAAW;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IAEN,QAAQ,KAA8B;AACpC,YAAM,iBAAiB,IAAI,YAAY,gBAAgB;AACvD,UAAI,QAAQ,IAAI,IAAI,QAAQ,cAAc,IAAI,QAAQ,YAAY,cAAc,GAAG,KAAK,CAAC;AACzF,UAAI,SAAS,IAAI,IAAI,QAAQ,YAAY,IAAI,OAAO,EAAE;AAAA,IACxD;AAAA,IAEA,MAAM,KAA8B;AAClC,YAAM,WAAW,IAAI,UAAU,IAAI,UAAU,IAAI,YAAY;AAC7D,UAAI,QAAQ,IAAI,IAAI,QAAQ,kBAAkB,QAAQ,IAAI;AAAA,IAC5D;AAAA,IAEA,QAAQ,OAAc,KAA8B;AAClD,YAAM,WAAW,KAAK,IAAI,IAAI,IAAI;AAClC,UAAI,SAAS,IAAI,IAAI,QAAQ,iBAAiB,QAAQ,OAAO,MAAM,OAAO,EAAE;AAAA,IAC9E;AAAA,IAEA,QAAQ,OAAc,KAA8B;AAClD,YAAM,WAAW,KAAK,IAAI,IAAI,IAAI;AAClC,UAAI,QAAQ,IAAI,IAAI,QAAQ,mBAAmB,QAAQ,OAAO,MAAM,OAAO,EAAE;AAAA,IAC/E;AAAA,IAEA,cAAc,OAAoB,KAAiC;AACjE,UAAI,iBAAiB;AACnB,YAAI,SAAS,iBAAiB,MAAM,IAAI,IAAI,EAAE,OAAO,MAAM,MAAM,CAAC;AAAA,MACpE;AACA,aAAO;AAAA,IACT;AAAA,IAEA,WAAW,MAAM,QAAQ,KAA8B;AACrD,UAAI,cAAc;AAChB,YAAI,QAAQ,IAAI,IAAI,QAAQ,gBAAgB,KAAK,IAAI,EAAE;AACvD,YAAI,SAAS,IAAI,IAAI,QAAQ,kBAAkB,EAAE,OAAO,CAAC;AAAA,MAC3D;AAAA,IACF;AAAA,IAEA,aAAa,MAAM,QAAQ,KAA8B;AACvD,UAAI,cAAc;AAChB,YAAI,SAAS,IAAI,IAAI,QAAQ,kBAAkB,KAAK,IAAI,IAAI,EAAE,OAAO,CAAC;AAAA,MACxE;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/middleware/logging.ts"],"sourcesContent":["/**\n * @fileoverview Logging middleware for request/response visibility.\n *\n * Provides configurable logging for LLM, embedding, and image operations,\n * including timing, error tracking, and optional event logging.\n *\n * @module middleware/logging\n */\n\nimport type { Middleware, MiddlewareContext, StreamContext } from '../types/middleware.ts';\nimport type { StreamEvent } from '../types/stream.ts';\n\n/**\n * Log levels for filtering output.\n */\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\n/**\n * Options for logging middleware.\n */\nexport interface LoggingOptions {\n /**\n * Minimum log level to output.\n * @default 'info'\n */\n level?: LogLevel;\n\n /**\n * Log individual stream events.\n * @default false\n */\n logStreamEvents?: boolean;\n\n /**\n * Log tool calls and results.\n * @default true\n */\n logToolCalls?: boolean;\n\n /**\n * Custom logger function. If not provided, uses console.log.\n * @param level - The log level\n * @param message - The log message\n * @param data - Optional additional data\n */\n logger?(level: LogLevel, message: string, data?: Record<string, unknown>): void;\n\n /**\n * Prefix for all log messages.\n * @default '[PP]'\n */\n prefix?: string;\n}\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\n/**\n * Creates a logging middleware for visibility into request lifecycle.\n *\n * This middleware logs the start, end, and errors of requests,\n * with optional logging of stream events and tool calls.\n *\n * @param options - Configuration options\n * @returns A middleware that logs request lifecycle events\n *\n * @example\n * ```typescript\n * import { llm } from '@providerprotocol/ai';\n * import { loggingMiddleware } from '@providerprotocol/ai/middleware/logging';\n * import { anthropic } from '@providerprotocol/ai/anthropic';\n *\n * const model = llm({\n * model: anthropic('claude-sonnet-4-20250514'),\n * middleware: [loggingMiddleware({ level: 'debug' })],\n * });\n *\n * // Logs: [PP] [anthropic] Starting llm request (streaming)\n * // Logs: [PP] [anthropic] Completed in 1234ms\n * const result = await model.generate('Hello');\n * ```\n */\nexport function loggingMiddleware(options: LoggingOptions = {}): Middleware {\n const {\n level = 'info',\n logStreamEvents = false,\n logToolCalls = true,\n logger,\n prefix = '[PP]',\n } = options;\n\n const minLevel = LOG_LEVELS[level];\n\n const log = (logLevel: LogLevel, message: string, data?: Record<string, unknown>) => {\n if (LOG_LEVELS[logLevel] < minLevel) {\n return;\n }\n\n const fullMessage = `${prefix} ${message}`;\n\n if (logger) {\n logger(logLevel, fullMessage, data);\n } else {\n const consoleMethod = logLevel === 'error' ? console.error : logLevel === 'warn' ? console.warn : console.log;\n if (data) {\n consoleMethod(fullMessage, data);\n } else {\n consoleMethod(fullMessage);\n }\n }\n };\n\n return {\n name: 'logging',\n\n onStart(ctx: MiddlewareContext): void {\n const streamingLabel = ctx.streaming ? '(streaming)' : '';\n log('info', `[${ctx.provider}] Starting ${ctx.modality} request ${streamingLabel}`.trim());\n log('debug', `[${ctx.provider}] Model: ${ctx.modelId}`);\n },\n\n onEnd(ctx: MiddlewareContext): void {\n const duration = ctx.endTime ? ctx.endTime - ctx.startTime : 0;\n log('info', `[${ctx.provider}] Completed in ${duration}ms`);\n },\n\n onError(error: Error, ctx: MiddlewareContext): void {\n const duration = Date.now() - ctx.startTime;\n log('error', `[${ctx.provider}] Error after ${duration}ms: ${error.message}`);\n },\n\n onAbort(error: Error, ctx: MiddlewareContext): void {\n const duration = Date.now() - ctx.startTime;\n log('warn', `[${ctx.provider}] Aborted after ${duration}ms: ${error.message}`);\n },\n\n onRetry(attempt: number, error: Error, ctx: MiddlewareContext): void {\n log('warn', `[${ctx.provider}] Retry attempt ${attempt}: ${error.message}`);\n },\n\n onStreamEvent(event: StreamEvent, ctx: StreamContext): StreamEvent {\n if (logStreamEvents) {\n log('debug', `Stream event: ${event.type}`, { index: event.index });\n }\n return event;\n },\n\n onToolCall(tool, params, ctx: MiddlewareContext): void {\n if (logToolCalls) {\n log('info', `[${ctx.provider}] Tool call: ${tool.name}`);\n log('debug', `[${ctx.provider}] Tool params:`, { params });\n }\n },\n\n onToolResult(tool, result, ctx: MiddlewareContext): void {\n if (logToolCalls) {\n log('debug', `[${ctx.provider}] Tool result: ${tool.name}`, { result });\n }\n },\n };\n}\n"],"mappings":";AAsDA,IAAM,aAAuC;AAAA,EAC3C,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AA2BO,SAAS,kBAAkB,UAA0B,CAAC,GAAe;AAC1E,QAAM;AAAA,IACJ,QAAQ;AAAA,IACR,kBAAkB;AAAA,IAClB,eAAe;AAAA,IACf;AAAA,IACA,SAAS;AAAA,EACX,IAAI;AAEJ,QAAM,WAAW,WAAW,KAAK;AAEjC,QAAM,MAAM,CAAC,UAAoB,SAAiB,SAAmC;AACnF,QAAI,WAAW,QAAQ,IAAI,UAAU;AACnC;AAAA,IACF;AAEA,UAAM,cAAc,GAAG,MAAM,IAAI,OAAO;AAExC,QAAI,QAAQ;AACV,aAAO,UAAU,aAAa,IAAI;AAAA,IACpC,OAAO;AACL,YAAM,gBAAgB,aAAa,UAAU,QAAQ,QAAQ,aAAa,SAAS,QAAQ,OAAO,QAAQ;AAC1G,UAAI,MAAM;AACR,sBAAc,aAAa,IAAI;AAAA,MACjC,OAAO;AACL,sBAAc,WAAW;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IAEN,QAAQ,KAA8B;AACpC,YAAM,iBAAiB,IAAI,YAAY,gBAAgB;AACvD,UAAI,QAAQ,IAAI,IAAI,QAAQ,cAAc,IAAI,QAAQ,YAAY,cAAc,GAAG,KAAK,CAAC;AACzF,UAAI,SAAS,IAAI,IAAI,QAAQ,YAAY,IAAI,OAAO,EAAE;AAAA,IACxD;AAAA,IAEA,MAAM,KAA8B;AAClC,YAAM,WAAW,IAAI,UAAU,IAAI,UAAU,IAAI,YAAY;AAC7D,UAAI,QAAQ,IAAI,IAAI,QAAQ,kBAAkB,QAAQ,IAAI;AAAA,IAC5D;AAAA,IAEA,QAAQ,OAAc,KAA8B;AAClD,YAAM,WAAW,KAAK,IAAI,IAAI,IAAI;AAClC,UAAI,SAAS,IAAI,IAAI,QAAQ,iBAAiB,QAAQ,OAAO,MAAM,OAAO,EAAE;AAAA,IAC9E;AAAA,IAEA,QAAQ,OAAc,KAA8B;AAClD,YAAM,WAAW,KAAK,IAAI,IAAI,IAAI;AAClC,UAAI,QAAQ,IAAI,IAAI,QAAQ,mBAAmB,QAAQ,OAAO,MAAM,OAAO,EAAE;AAAA,IAC/E;AAAA,IAEA,QAAQ,SAAiB,OAAc,KAA8B;AACnE,UAAI,QAAQ,IAAI,IAAI,QAAQ,mBAAmB,OAAO,KAAK,MAAM,OAAO,EAAE;AAAA,IAC5E;AAAA,IAEA,cAAc,OAAoB,KAAiC;AACjE,UAAI,iBAAiB;AACnB,YAAI,SAAS,iBAAiB,MAAM,IAAI,IAAI,EAAE,OAAO,MAAM,MAAM,CAAC;AAAA,MACpE;AACA,aAAO;AAAA,IACT;AAAA,IAEA,WAAW,MAAM,QAAQ,KAA8B;AACrD,UAAI,cAAc;AAChB,YAAI,QAAQ,IAAI,IAAI,QAAQ,gBAAgB,KAAK,IAAI,EAAE;AACvD,YAAI,SAAS,IAAI,IAAI,QAAQ,kBAAkB,EAAE,OAAO,CAAC;AAAA,MAC3D;AAAA,IACF;AAAA,IAEA,aAAa,MAAM,QAAQ,KAA8B;AACvD,UAAI,cAAc;AAChB,YAAI,SAAS,IAAI,IAAI,QAAQ,kBAAkB,KAAK,IAAI,IAAI,EAAE,OAAO,CAAC;AAAA,MACxE;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { M as Middleware } from '../../llm-
|
|
2
|
-
import { E as EventDelta, S as StreamEvent } from '../../stream-
|
|
1
|
+
import { M as Middleware } from '../../llm-BWLaTzzY.js';
|
|
2
|
+
import { E as EventDelta, S as StreamEvent } from '../../stream-bBd_4Ipu.js';
|
|
3
|
+
import '../../tool-BmAfKNBq.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* @fileoverview Parsed object middleware for incremental JSON parsing.
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "../../chunk-I53CI6ZZ.js";
|
|
4
4
|
import {
|
|
5
5
|
StreamEventType
|
|
6
|
-
} from "../../chunk-
|
|
6
|
+
} from "../../chunk-F5ENANMJ.js";
|
|
7
7
|
|
|
8
8
|
// src/middleware/parsed-object.ts
|
|
9
9
|
var ACCUMULATED_TEXT_KEY = "parsedObject:text";
|
|
@@ -64,6 +64,10 @@ function parsedObjectMiddleware(options = {}) {
|
|
|
64
64
|
onStreamEnd(ctx) {
|
|
65
65
|
ctx.state.delete(ACCUMULATED_TEXT_KEY);
|
|
66
66
|
ctx.state.delete(ACCUMULATED_ARGS_KEY);
|
|
67
|
+
},
|
|
68
|
+
onRetry(_attempt, _error, ctx) {
|
|
69
|
+
ctx.state.delete(ACCUMULATED_TEXT_KEY);
|
|
70
|
+
ctx.state.delete(ACCUMULATED_ARGS_KEY);
|
|
67
71
|
}
|
|
68
72
|
};
|
|
69
73
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/middleware/parsed-object.ts"],"sourcesContent":["/**\n * @fileoverview Parsed object middleware for incremental JSON parsing.\n *\n * This middleware parses partial JSON from ObjectDelta and ToolCallDelta\n * stream events, providing incremental structured data during streaming.\n *\n * @module middleware/parsed-object\n */\n\nimport type { Middleware, StreamContext } from '../types/middleware.ts';\nimport type { EventDelta, StreamEvent } from '../types/stream.ts';\nimport { StreamEventType } from '../types/stream.ts';\nimport { parsePartialJson } from '../utils/partial-json.ts';\n\n/**\n * Event delta with parsed JSON data.\n * Extended by parsedObjectMiddleware when parsing is enabled.\n */\nexport interface ParsedEventDelta extends EventDelta {\n /** Incrementally parsed JSON value */\n parsed?: unknown;\n}\n\n/**\n * Stream event with parsed JSON data.\n * Returned by parsedObjectMiddleware for ObjectDelta and ToolCallDelta events.\n */\nexport interface ParsedStreamEvent extends Omit<StreamEvent, 'delta'> {\n delta: ParsedEventDelta;\n}\n\n/**\n * Options for parsed object middleware.\n */\nexport interface ParsedObjectOptions {\n /**\n * Parse ObjectDelta events (structured output responses).\n * @default true\n */\n parseObjects?: boolean;\n\n /**\n * Parse ToolCallDelta events (tool call arguments).\n * @default true\n */\n parseToolCalls?: boolean;\n}\n\n/**\n * Creates a middleware that parses partial JSON from stream events.\n *\n * This middleware accumulates text from ObjectDelta events and tool\n * argument JSON from ToolCallDelta events, then parses them incrementally\n * using partial JSON parsing. The parsed result is added to the event's\n * `parsed` field.\n *\n * @param options - Configuration options\n * @returns A middleware that adds parsed JSON to stream events\n *\n * @example\n * ```typescript\n * import { llm } from '@providerprotocol/ai';\n * import { parsedObjectMiddleware } from '@providerprotocol/ai/middleware/parsed-object';\n * import { anthropic } from '@providerprotocol/ai/anthropic';\n *\n * const model = llm({\n * model: anthropic('claude-sonnet-4-20250514'),\n * structure: mySchema,\n * middleware: [parsedObjectMiddleware()],\n * });\n *\n * for await (const event of model.stream('Extract data from this text')) {\n * if (event.type === 'object_delta') {\n * // event.delta.parsed contains incrementally parsed object\n * console.log(event.delta.parsed);\n * }\n * }\n * ```\n */\n/** State key for accumulated object text */\nconst ACCUMULATED_TEXT_KEY = 'parsedObject:text';\n/** State key for accumulated tool arguments */\nconst ACCUMULATED_ARGS_KEY = 'parsedObject:args';\n\n/**\n * Gets or creates the accumulated text map from state.\n */\nfunction getAccumulatedText(state: Map<string, unknown>): Map<number, string> {\n let map = state.get(ACCUMULATED_TEXT_KEY) as Map<number, string> | undefined;\n if (!map) {\n map = new Map();\n state.set(ACCUMULATED_TEXT_KEY, map);\n }\n return map;\n}\n\n/**\n * Gets or creates the accumulated args map from state.\n */\nfunction getAccumulatedArgs(state: Map<string, unknown>): Map<number, string> {\n let map = state.get(ACCUMULATED_ARGS_KEY) as Map<number, string> | undefined;\n if (!map) {\n map = new Map();\n state.set(ACCUMULATED_ARGS_KEY, map);\n }\n return map;\n}\n\nexport function parsedObjectMiddleware(options: ParsedObjectOptions = {}): Middleware {\n const { parseObjects = true, parseToolCalls = true } = options;\n\n return {\n name: 'parsed-object',\n\n onStreamEvent(event: StreamEvent, ctx: StreamContext): StreamEvent | StreamEvent[] | null {\n if (parseObjects && event.type === StreamEventType.ObjectDelta) {\n const accumulatedText = getAccumulatedText(ctx.state);\n const current = accumulatedText.get(event.index) ?? '';\n const newText = current + (event.delta.text ?? '');\n accumulatedText.set(event.index, newText);\n\n const parseResult = parsePartialJson(newText);\n\n const parsedEvent: ParsedStreamEvent = {\n ...event,\n delta: {\n ...event.delta,\n parsed: parseResult.value,\n },\n };\n return parsedEvent as StreamEvent;\n }\n\n if (parseToolCalls && event.type === StreamEventType.ToolCallDelta) {\n const accumulatedArgs = getAccumulatedArgs(ctx.state);\n const current = accumulatedArgs.get(event.index) ?? '';\n const newJson = current + (event.delta.argumentsJson ?? '');\n accumulatedArgs.set(event.index, newJson);\n\n const parseResult = parsePartialJson(newJson);\n\n const parsedEvent: ParsedStreamEvent = {\n ...event,\n delta: {\n ...event.delta,\n parsed: parseResult.value,\n },\n };\n return parsedEvent as StreamEvent;\n }\n\n return event;\n },\n\n onStreamEnd(ctx: StreamContext): void {\n
|
|
1
|
+
{"version":3,"sources":["../../../src/middleware/parsed-object.ts"],"sourcesContent":["/**\n * @fileoverview Parsed object middleware for incremental JSON parsing.\n *\n * This middleware parses partial JSON from ObjectDelta and ToolCallDelta\n * stream events, providing incremental structured data during streaming.\n *\n * @module middleware/parsed-object\n */\n\nimport type { Middleware, MiddlewareContext, StreamContext } from '../types/middleware.ts';\nimport type { EventDelta, StreamEvent } from '../types/stream.ts';\nimport { StreamEventType } from '../types/stream.ts';\nimport { parsePartialJson } from '../utils/partial-json.ts';\n\n/**\n * Event delta with parsed JSON data.\n * Extended by parsedObjectMiddleware when parsing is enabled.\n */\nexport interface ParsedEventDelta extends EventDelta {\n /** Incrementally parsed JSON value */\n parsed?: unknown;\n}\n\n/**\n * Stream event with parsed JSON data.\n * Returned by parsedObjectMiddleware for ObjectDelta and ToolCallDelta events.\n */\nexport interface ParsedStreamEvent extends Omit<StreamEvent, 'delta'> {\n delta: ParsedEventDelta;\n}\n\n/**\n * Options for parsed object middleware.\n */\nexport interface ParsedObjectOptions {\n /**\n * Parse ObjectDelta events (structured output responses).\n * @default true\n */\n parseObjects?: boolean;\n\n /**\n * Parse ToolCallDelta events (tool call arguments).\n * @default true\n */\n parseToolCalls?: boolean;\n}\n\n/**\n * Creates a middleware that parses partial JSON from stream events.\n *\n * This middleware accumulates text from ObjectDelta events and tool\n * argument JSON from ToolCallDelta events, then parses them incrementally\n * using partial JSON parsing. The parsed result is added to the event's\n * `parsed` field.\n *\n * @param options - Configuration options\n * @returns A middleware that adds parsed JSON to stream events\n *\n * @example\n * ```typescript\n * import { llm } from '@providerprotocol/ai';\n * import { parsedObjectMiddleware } from '@providerprotocol/ai/middleware/parsed-object';\n * import { anthropic } from '@providerprotocol/ai/anthropic';\n *\n * const model = llm({\n * model: anthropic('claude-sonnet-4-20250514'),\n * structure: mySchema,\n * middleware: [parsedObjectMiddleware()],\n * });\n *\n * for await (const event of model.stream('Extract data from this text')) {\n * if (event.type === 'object_delta') {\n * // event.delta.parsed contains incrementally parsed object\n * console.log(event.delta.parsed);\n * }\n * }\n * ```\n */\n/** State key for accumulated object text */\nconst ACCUMULATED_TEXT_KEY = 'parsedObject:text';\n/** State key for accumulated tool arguments */\nconst ACCUMULATED_ARGS_KEY = 'parsedObject:args';\n\n/**\n * Gets or creates the accumulated text map from state.\n */\nfunction getAccumulatedText(state: Map<string, unknown>): Map<number, string> {\n let map = state.get(ACCUMULATED_TEXT_KEY) as Map<number, string> | undefined;\n if (!map) {\n map = new Map();\n state.set(ACCUMULATED_TEXT_KEY, map);\n }\n return map;\n}\n\n/**\n * Gets or creates the accumulated args map from state.\n */\nfunction getAccumulatedArgs(state: Map<string, unknown>): Map<number, string> {\n let map = state.get(ACCUMULATED_ARGS_KEY) as Map<number, string> | undefined;\n if (!map) {\n map = new Map();\n state.set(ACCUMULATED_ARGS_KEY, map);\n }\n return map;\n}\n\nexport function parsedObjectMiddleware(options: ParsedObjectOptions = {}): Middleware {\n const { parseObjects = true, parseToolCalls = true } = options;\n\n return {\n name: 'parsed-object',\n\n onStreamEvent(event: StreamEvent, ctx: StreamContext): StreamEvent | StreamEvent[] | null {\n if (parseObjects && event.type === StreamEventType.ObjectDelta) {\n const accumulatedText = getAccumulatedText(ctx.state);\n const current = accumulatedText.get(event.index) ?? '';\n const newText = current + (event.delta.text ?? '');\n accumulatedText.set(event.index, newText);\n\n const parseResult = parsePartialJson(newText);\n\n const parsedEvent: ParsedStreamEvent = {\n ...event,\n delta: {\n ...event.delta,\n parsed: parseResult.value,\n },\n };\n return parsedEvent as StreamEvent;\n }\n\n if (parseToolCalls && event.type === StreamEventType.ToolCallDelta) {\n const accumulatedArgs = getAccumulatedArgs(ctx.state);\n const current = accumulatedArgs.get(event.index) ?? '';\n const newJson = current + (event.delta.argumentsJson ?? '');\n accumulatedArgs.set(event.index, newJson);\n\n const parseResult = parsePartialJson(newJson);\n\n const parsedEvent: ParsedStreamEvent = {\n ...event,\n delta: {\n ...event.delta,\n parsed: parseResult.value,\n },\n };\n return parsedEvent as StreamEvent;\n }\n\n return event;\n },\n\n onStreamEnd(ctx: StreamContext): void {\n ctx.state.delete(ACCUMULATED_TEXT_KEY);\n ctx.state.delete(ACCUMULATED_ARGS_KEY);\n },\n\n onRetry(_attempt: number, _error: Error, ctx: MiddlewareContext): void {\n ctx.state.delete(ACCUMULATED_TEXT_KEY);\n ctx.state.delete(ACCUMULATED_ARGS_KEY);\n },\n };\n}\n"],"mappings":";;;;;;;;AAgFA,IAAM,uBAAuB;AAE7B,IAAM,uBAAuB;AAK7B,SAAS,mBAAmB,OAAkD;AAC5E,MAAI,MAAM,MAAM,IAAI,oBAAoB;AACxC,MAAI,CAAC,KAAK;AACR,UAAM,oBAAI,IAAI;AACd,UAAM,IAAI,sBAAsB,GAAG;AAAA,EACrC;AACA,SAAO;AACT;AAKA,SAAS,mBAAmB,OAAkD;AAC5E,MAAI,MAAM,MAAM,IAAI,oBAAoB;AACxC,MAAI,CAAC,KAAK;AACR,UAAM,oBAAI,IAAI;AACd,UAAM,IAAI,sBAAsB,GAAG;AAAA,EACrC;AACA,SAAO;AACT;AAEO,SAAS,uBAAuB,UAA+B,CAAC,GAAe;AACpF,QAAM,EAAE,eAAe,MAAM,iBAAiB,KAAK,IAAI;AAEvD,SAAO;AAAA,IACL,MAAM;AAAA,IAEN,cAAc,OAAoB,KAAwD;AACxF,UAAI,gBAAgB,MAAM,SAAS,gBAAgB,aAAa;AAC9D,cAAM,kBAAkB,mBAAmB,IAAI,KAAK;AACpD,cAAM,UAAU,gBAAgB,IAAI,MAAM,KAAK,KAAK;AACpD,cAAM,UAAU,WAAW,MAAM,MAAM,QAAQ;AAC/C,wBAAgB,IAAI,MAAM,OAAO,OAAO;AAExC,cAAM,cAAc,iBAAiB,OAAO;AAE5C,cAAM,cAAiC;AAAA,UACrC,GAAG;AAAA,UACH,OAAO;AAAA,YACL,GAAG,MAAM;AAAA,YACT,QAAQ,YAAY;AAAA,UACtB;AAAA,QACF;AACA,eAAO;AAAA,MACT;AAEA,UAAI,kBAAkB,MAAM,SAAS,gBAAgB,eAAe;AAClE,cAAM,kBAAkB,mBAAmB,IAAI,KAAK;AACpD,cAAM,UAAU,gBAAgB,IAAI,MAAM,KAAK,KAAK;AACpD,cAAM,UAAU,WAAW,MAAM,MAAM,iBAAiB;AACxD,wBAAgB,IAAI,MAAM,OAAO,OAAO;AAExC,cAAM,cAAc,iBAAiB,OAAO;AAE5C,cAAM,cAAiC;AAAA,UACrC,GAAG;AAAA,UACH,OAAO;AAAA,YACL,GAAG,MAAM;AAAA,YACT,QAAQ,YAAY;AAAA,UACtB;AAAA,QACF;AACA,eAAO;AAAA,MACT;AAEA,aAAO;AAAA,IACT;AAAA,IAEA,YAAY,KAA0B;AACpC,UAAI,MAAM,OAAO,oBAAoB;AACrC,UAAI,MAAM,OAAO,oBAAoB;AAAA,IACvC;AAAA,IAEA,QAAQ,UAAkB,QAAe,KAA8B;AACrE,UAAI,MAAM,OAAO,oBAAoB;AACrC,UAAI,MAAM,OAAO,oBAAoB;AAAA,IACvC;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { T as Thread, a as ThreadJSON, M as Middleware } from '../../llm-
|
|
2
|
-
import { T as Turn } from '../../stream-
|
|
1
|
+
import { T as Thread, a as ThreadJSON, M as Middleware } from '../../llm-BWLaTzzY.js';
|
|
2
|
+
import { T as Turn } from '../../stream-bBd_4Ipu.js';
|
|
3
|
+
import '../../tool-BmAfKNBq.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* @fileoverview Persistence middleware for thread storage.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/middleware/persistence.ts"],"sourcesContent":["/**\n * @fileoverview Persistence middleware for thread storage.\n *\n * Loads a conversation thread before execution and saves it after completion.\n * Designed for LLM requests; other modalities are ignored.\n *\n * @module middleware/persistence\n */\n\nimport type { Middleware, MiddlewareContext } from '../types/middleware.ts';\nimport type { LLMRequest } from '../types/llm.ts';\nimport type { Turn } from '../types/turn.ts';\nimport type { ThreadJSON } from '../types/thread.ts';\nimport { Thread } from '../types/thread.ts';\nimport { toError } from '../utils/error.ts';\n\nconst STATE_KEY_THREAD = 'persistence:thread';\nconst STATE_KEY_ID = 'persistence:id';\nconst TURN_START_INDEX_KEY = 'llm:turnStartIndex';\n\nconst isLLMRequest = (request: MiddlewareContext['request']): request is LLMRequest => (\n 'messages' in request\n);\n\n/**\n * Load result for persistence adapters.\n */\nexport type PersistenceLoadResult = Thread | ThreadJSON | null | undefined;\n\n/**\n * Adapter configuration for persistence middleware.\n */\nexport interface PersistenceAdapterConfig {\n /**\n * Unique identifier for the conversation.\n */\n id: string;\n\n /**\n * Loads a thread for the provided ID.\n *\n * Return a Thread instance, ThreadJSON, or null/undefined for new threads.\n *\n * @param id - Conversation identifier\n */\n load(id: string): Promise<PersistenceLoadResult>;\n\n /**\n * Persists the thread after a turn completes.\n *\n * @param id - Conversation identifier\n * @param thread - Updated thread instance\n * @param turn - Completed turn (undefined if not available)\n */\n save(id: string, thread: Thread, turn: Turn | undefined): Promise<void>;\n}\n\n/**\n * Persistence adapter implementation.\n *\n * Provides a thin wrapper around load/save callbacks.\n */\nexport class PersistenceAdapter {\n readonly id: string;\n\n private readonly loader: PersistenceAdapterConfig['load'];\n\n private readonly saver: PersistenceAdapterConfig['save'];\n\n /**\n * Creates a persistence adapter.\n *\n * @param config - Adapter configuration\n */\n constructor(config: PersistenceAdapterConfig) {\n this.id = config.id;\n this.loader = config.load;\n this.saver = config.save;\n }\n\n /**\n * Loads a thread for the provided ID.\n *\n * @param id - Conversation identifier\n */\n async load(id: string): Promise<PersistenceLoadResult> {\n return this.loader(id);\n }\n\n /**\n * Persists the thread after a turn completes.\n *\n * @param id - Conversation identifier\n * @param thread - Updated thread instance\n * @param turn - Completed turn (undefined if not available)\n */\n async save(id: string, thread: Thread, turn: Turn | undefined): Promise<void> {\n await this.saver(id, thread, turn);\n }\n}\n\n/**\n * Options for persistence middleware.\n */\nexport interface PersistenceOptions {\n /**\n * Adapter instance for loading and saving threads.\n */\n adapter: PersistenceAdapter;\n}\n\n/**\n * Gets the loaded thread from middleware state.\n *\n * @param state - Middleware state map\n * @returns Thread instance or undefined if not set\n */\nexport function getThread(state: Map<string, unknown>): Thread | undefined {\n return state.get(STATE_KEY_THREAD) as Thread | undefined;\n}\n\n/**\n * Gets the conversation ID from middleware state.\n *\n * @param state - Middleware state map\n * @returns Conversation ID or undefined if not set\n */\nexport function getThreadId(state: Map<string, unknown>): string | undefined {\n return state.get(STATE_KEY_ID) as string | undefined;\n}\n\n/**\n * Creates persistence middleware for thread storage.\n *\n * Loads a thread before requests and saves it after completion. The middleware\n * prepends loaded messages that are not already present in the request so turn\n * slicing excludes persisted history without duplicating explicit history.\n *\n * @param options - Middleware configuration\n * @returns Middleware instance\n *\n * @example\n * ```typescript\n * import { llm } from '@providerprotocol/ai';\n * import { anthropic } from '@providerprotocol/ai/anthropic';\n * import { persistenceMiddleware, PersistenceAdapter } from '@providerprotocol/ai/middleware/persistence';\n *\n * const model = llm({\n * model: anthropic('claude-sonnet-4-20250514'),\n * system: 'You are a helpful assistant.',\n * middleware: [\n * persistenceMiddleware({\n * adapter: new PersistenceAdapter({\n * id: 'conversation-id',\n * load: async (id) => loadThreadFromMemory(id),\n * save: async (id, thread) => saveThreadToMemory(id, thread),\n * }),\n * }),\n * ],\n * });\n * ```\n */\nexport function persistenceMiddleware(options: PersistenceOptions): Middleware {\n const { adapter } = options;\n\n if (!adapter?.id) {\n throw new Error('persistenceMiddleware requires an adapter with a non-empty id');\n }\n if (typeof adapter.load !== 'function' || typeof adapter.save !== 'function') {\n throw new Error('persistenceMiddleware requires an adapter with load and save functions');\n }\n\n return {\n name: 'persistence',\n\n async onRequest(ctx: MiddlewareContext): Promise<void> {\n if (ctx.modality !== 'llm' || !isLLMRequest(ctx.request)) {\n return;\n }\n\n ctx.state.set(STATE_KEY_ID, adapter.id);\n\n let loaded: PersistenceLoadResult;\n try {\n loaded = await adapter.load(adapter.id);\n } catch (error) {\n const err = toError(error);\n throw new Error(`Persistence adapter failed to load thread \"${adapter.id}\": ${err.message}`, {\n cause: err,\n });\n }\n\n let thread: Thread;\n if (!loaded) {\n thread = new Thread();\n } else if (loaded instanceof Thread) {\n thread = loaded;\n } else {\n try {\n thread = Thread.fromJSON(loaded);\n } catch (error) {\n const err = toError(error);\n throw new Error(`Persistence adapter failed to deserialize thread \"${adapter.id}\": ${err.message}`, {\n cause: err,\n });\n }\n }\n\n ctx.state.set(STATE_KEY_THREAD, thread);\n\n if (thread.messages.length > 0) {\n const existingIds = new Set(ctx.request.messages.map((message) => message.id));\n const missing = thread.messages.filter((message) => !existingIds.has(message.id));\n if (missing.length > 0) {\n ctx.request.messages.unshift(...missing);\n const currentIndex = ctx.state.get(TURN_START_INDEX_KEY);\n const nextIndex = (typeof currentIndex === 'number' ? currentIndex : 0) + missing.length;\n ctx.state.set(TURN_START_INDEX_KEY, nextIndex);\n }\n }\n },\n\n async onTurn(turn: Turn, ctx: MiddlewareContext): Promise<void> {\n if (ctx.modality !== 'llm') {\n return;\n }\n\n const thread = getThread(ctx.state);\n if (!thread) {\n return;\n }\n\n if (isLLMRequest(ctx.request)) {\n const turnMessageIds = new Set(turn.messages.map((message) => message.id));\n const existingIds = new Set(thread.messages.map((message) => message.id));\n for (const message of ctx.request.messages) {\n if (turnMessageIds.has(message.id)) {\n continue;\n }\n if (!existingIds.has(message.id)) {\n thread.push(message);\n existingIds.add(message.id);\n }\n }\n }\n\n thread.append(turn);\n\n try {\n await adapter.save(adapter.id, thread, turn);\n } catch (error) {\n const err = toError(error);\n throw new Error(`Persistence adapter failed to save thread \"${adapter.id}\": ${err.message}`, {\n cause: err,\n });\n }\n },\n };\n}\n"],"mappings":";;;;;;;;;;AAgBA,IAAM,mBAAmB;AACzB,IAAM,eAAe;AACrB,IAAM,uBAAuB;AAE7B,IAAM,eAAe,CAAC,YACpB,cAAc;AAyCT,IAAM,qBAAN,MAAyB;AAAA,EACrB;AAAA,EAEQ;AAAA,EAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOjB,YAAY,QAAkC;AAC5C,SAAK,KAAK,OAAO;AACjB,SAAK,SAAS,OAAO;AACrB,SAAK,QAAQ,OAAO;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,KAAK,IAA4C;AACrD,WAAO,KAAK,OAAO,EAAE;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,KAAK,IAAY,QAAgB,MAAuC;AAC5E,UAAM,KAAK,MAAM,IAAI,QAAQ,IAAI;AAAA,EACnC;AACF;AAkBO,SAAS,UAAU,OAAiD;AACzE,SAAO,MAAM,IAAI,gBAAgB;AACnC;AAQO,SAAS,YAAY,OAAiD;AAC3E,SAAO,MAAM,IAAI,YAAY;AAC/B;AAiCO,SAAS,sBAAsB,SAAyC;AAC7E,QAAM,EAAE,QAAQ,IAAI;AAEpB,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,+DAA+D;AAAA,EACjF;AACA,MAAI,OAAO,QAAQ,SAAS,cAAc,OAAO,QAAQ,SAAS,YAAY;AAC5E,UAAM,IAAI,MAAM,wEAAwE;AAAA,EAC1F;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IAEN,MAAM,UAAU,KAAuC;AACrD,UAAI,IAAI,aAAa,SAAS,CAAC,aAAa,IAAI,OAAO,GAAG;AACxD;AAAA,MACF;AAEA,UAAI,MAAM,IAAI,cAAc,QAAQ,EAAE;AAEtC,UAAI;AACJ,UAAI;AACF,iBAAS,MAAM,QAAQ,KAAK,QAAQ,EAAE;AAAA,MACxC,SAAS,OAAO;AACd,cAAM,MAAM,QAAQ,KAAK;AACzB,cAAM,IAAI,MAAM,8CAA8C,QAAQ,EAAE,MAAM,IAAI,OAAO,IAAI;AAAA,UAC3F,OAAO;AAAA,QACT,CAAC;AAAA,MACH;AAEA,UAAI;AACJ,UAAI,CAAC,QAAQ;AACX,iBAAS,IAAI,OAAO;AAAA,MACtB,WAAW,kBAAkB,QAAQ;AACnC,iBAAS;AAAA,MACX,OAAO;AACL,YAAI;AACF,mBAAS,OAAO,SAAS,MAAM;AAAA,QACjC,SAAS,OAAO;AACd,gBAAM,MAAM,QAAQ,KAAK;AACzB,gBAAM,IAAI,MAAM,qDAAqD,QAAQ,EAAE,MAAM,IAAI,OAAO,IAAI;AAAA,YAClG,OAAO;AAAA,UACT,CAAC;AAAA,QACH;AAAA,MACF;AAEA,UAAI,MAAM,IAAI,kBAAkB,MAAM;AAEtC,UAAI,OAAO,SAAS,SAAS,GAAG;AAC9B,cAAM,cAAc,IAAI,IAAI,IAAI,QAAQ,SAAS,IAAI,CAAC,YAAY,QAAQ,EAAE,CAAC;AAC7E,cAAM,UAAU,OAAO,SAAS,OAAO,CAAC,YAAY,CAAC,YAAY,IAAI,QAAQ,EAAE,CAAC;AAChF,YAAI,QAAQ,SAAS,GAAG;AACtB,cAAI,QAAQ,SAAS,QAAQ,GAAG,OAAO;AACvC,gBAAM,eAAe,IAAI,MAAM,IAAI,oBAAoB;AACvD,gBAAM,aAAa,OAAO,iBAAiB,WAAW,eAAe,KAAK,QAAQ;AAClF,cAAI,MAAM,IAAI,sBAAsB,SAAS;AAAA,QAC/C;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,OAAO,MAAY,KAAuC;AAC9D,UAAI,IAAI,aAAa,OAAO;AAC1B;AAAA,MACF;AAEA,YAAM,SAAS,UAAU,IAAI,KAAK;AAClC,UAAI,CAAC,QAAQ;AACX;AAAA,MACF;AAEA,UAAI,aAAa,IAAI,OAAO,GAAG;AAC7B,cAAM,iBAAiB,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,YAAY,QAAQ,EAAE,CAAC;AACzE,cAAM,cAAc,IAAI,IAAI,OAAO,SAAS,IAAI,CAAC,YAAY,QAAQ,EAAE,CAAC;AACxE,mBAAW,WAAW,IAAI,QAAQ,UAAU;AAC1C,cAAI,eAAe,IAAI,QAAQ,EAAE,GAAG;AAClC;AAAA,UACF;AACA,cAAI,CAAC,YAAY,IAAI,QAAQ,EAAE,GAAG;AAChC,mBAAO,KAAK,OAAO;AACnB,wBAAY,IAAI,QAAQ,EAAE;AAAA,UAC5B;AAAA,QACF;AAAA,MACF;AAEA,aAAO,OAAO,IAAI;AAElB,UAAI;AACF,cAAM,QAAQ,KAAK,QAAQ,IAAI,QAAQ,IAAI;AAAA,MAC7C,SAAS,OAAO;AACd,cAAM,MAAM,QAAQ,KAAK;AACzB,cAAM,IAAI,MAAM,8CAA8C,QAAQ,EAAE,MAAM,IAAI,OAAO,IAAI;AAAA,UAC3F,OAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/middleware/persistence.ts"],"sourcesContent":["/**\n * @fileoverview Persistence middleware for thread storage.\n *\n * Loads a conversation thread before execution and saves it after completion.\n * Designed for LLM requests; other modalities are ignored.\n *\n * @module middleware/persistence\n */\n\nimport type { Middleware, MiddlewareContext } from '../types/middleware.ts';\nimport type { LLMRequest } from '../types/llm.ts';\nimport type { Turn } from '../types/turn.ts';\nimport type { ThreadJSON } from '../types/thread.ts';\nimport { Thread } from '../types/thread.ts';\nimport { toError } from '../utils/error.ts';\n\nconst STATE_KEY_THREAD = 'persistence:thread';\nconst STATE_KEY_ID = 'persistence:id';\nconst TURN_START_INDEX_KEY = 'llm:turnStartIndex';\n\nconst isLLMRequest = (request: MiddlewareContext['request']): request is LLMRequest => (\n 'messages' in request\n);\n\n/**\n * Load result for persistence adapters.\n */\nexport type PersistenceLoadResult = Thread | ThreadJSON | null | undefined;\n\n/**\n * Adapter configuration for persistence middleware.\n */\nexport interface PersistenceAdapterConfig {\n /**\n * Unique identifier for the conversation.\n */\n id: string;\n\n /**\n * Loads a thread for the provided ID.\n *\n * Return a Thread instance, ThreadJSON, or null/undefined for new threads.\n *\n * @param id - Conversation identifier\n */\n load(id: string): Promise<PersistenceLoadResult>;\n\n /**\n * Persists the thread after a turn completes.\n *\n * @param id - Conversation identifier\n * @param thread - Updated thread instance\n * @param turn - Completed turn (undefined if not available)\n */\n save(id: string, thread: Thread, turn: Turn | undefined): Promise<void>;\n}\n\n/**\n * Persistence adapter implementation.\n *\n * Provides a thin wrapper around load/save callbacks.\n */\nexport class PersistenceAdapter {\n readonly id: string;\n\n private readonly loader: PersistenceAdapterConfig['load'];\n\n private readonly saver: PersistenceAdapterConfig['save'];\n\n /**\n * Creates a persistence adapter.\n *\n * @param config - Adapter configuration\n */\n constructor(config: PersistenceAdapterConfig) {\n this.id = config.id;\n this.loader = config.load;\n this.saver = config.save;\n }\n\n /**\n * Loads a thread for the provided ID.\n *\n * @param id - Conversation identifier\n */\n async load(id: string): Promise<PersistenceLoadResult> {\n return this.loader(id);\n }\n\n /**\n * Persists the thread after a turn completes.\n *\n * @param id - Conversation identifier\n * @param thread - Updated thread instance\n * @param turn - Completed turn (undefined if not available)\n */\n async save(id: string, thread: Thread, turn: Turn | undefined): Promise<void> {\n await this.saver(id, thread, turn);\n }\n}\n\n/**\n * Options for persistence middleware.\n */\nexport interface PersistenceOptions {\n /**\n * Adapter instance for loading and saving threads.\n */\n adapter: PersistenceAdapter;\n}\n\n/**\n * Gets the loaded thread from middleware state.\n *\n * @param state - Middleware state map\n * @returns Thread instance or undefined if not set\n */\nexport function getThread(state: Map<string, unknown>): Thread | undefined {\n return state.get(STATE_KEY_THREAD) as Thread | undefined;\n}\n\n/**\n * Gets the conversation ID from middleware state.\n *\n * @param state - Middleware state map\n * @returns Conversation ID or undefined if not set\n */\nexport function getThreadId(state: Map<string, unknown>): string | undefined {\n return state.get(STATE_KEY_ID) as string | undefined;\n}\n\n/**\n * Creates persistence middleware for thread storage.\n *\n * Loads a thread before requests and saves it after completion. The middleware\n * prepends loaded messages that are not already present in the request so turn\n * slicing excludes persisted history without duplicating explicit history.\n *\n * @param options - Middleware configuration\n * @returns Middleware instance\n *\n * @example\n * ```typescript\n * import { llm } from '@providerprotocol/ai';\n * import { anthropic } from '@providerprotocol/ai/anthropic';\n * import { persistenceMiddleware, PersistenceAdapter } from '@providerprotocol/ai/middleware/persistence';\n *\n * const model = llm({\n * model: anthropic('claude-sonnet-4-20250514'),\n * system: 'You are a helpful assistant.',\n * middleware: [\n * persistenceMiddleware({\n * adapter: new PersistenceAdapter({\n * id: 'conversation-id',\n * load: async (id) => loadThreadFromMemory(id),\n * save: async (id, thread) => saveThreadToMemory(id, thread),\n * }),\n * }),\n * ],\n * });\n * ```\n */\nexport function persistenceMiddleware(options: PersistenceOptions): Middleware {\n const { adapter } = options;\n\n if (!adapter?.id) {\n throw new Error('persistenceMiddleware requires an adapter with a non-empty id');\n }\n if (typeof adapter.load !== 'function' || typeof adapter.save !== 'function') {\n throw new Error('persistenceMiddleware requires an adapter with load and save functions');\n }\n\n return {\n name: 'persistence',\n\n async onRequest(ctx: MiddlewareContext): Promise<void> {\n if (ctx.modality !== 'llm' || !isLLMRequest(ctx.request)) {\n return;\n }\n\n ctx.state.set(STATE_KEY_ID, adapter.id);\n\n let loaded: PersistenceLoadResult;\n try {\n loaded = await adapter.load(adapter.id);\n } catch (error) {\n const err = toError(error);\n throw new Error(`Persistence adapter failed to load thread \"${adapter.id}\": ${err.message}`, {\n cause: err,\n });\n }\n\n let thread: Thread;\n if (!loaded) {\n thread = new Thread();\n } else if (loaded instanceof Thread) {\n thread = loaded;\n } else {\n try {\n thread = Thread.fromJSON(loaded);\n } catch (error) {\n const err = toError(error);\n throw new Error(`Persistence adapter failed to deserialize thread \"${adapter.id}\": ${err.message}`, {\n cause: err,\n });\n }\n }\n\n ctx.state.set(STATE_KEY_THREAD, thread);\n\n if (thread.messages.length > 0) {\n const existingIds = new Set(ctx.request.messages.map((message) => message.id));\n const missing = thread.messages.filter((message) => !existingIds.has(message.id));\n if (missing.length > 0) {\n ctx.request.messages.unshift(...missing);\n const currentIndex = ctx.state.get(TURN_START_INDEX_KEY);\n const nextIndex = (typeof currentIndex === 'number' ? currentIndex : 0) + missing.length;\n ctx.state.set(TURN_START_INDEX_KEY, nextIndex);\n }\n }\n },\n\n async onTurn(turn: Turn, ctx: MiddlewareContext): Promise<void> {\n if (ctx.modality !== 'llm') {\n return;\n }\n\n const thread = getThread(ctx.state);\n if (!thread) {\n return;\n }\n\n if (isLLMRequest(ctx.request)) {\n const turnMessageIds = new Set(turn.messages.map((message) => message.id));\n const existingIds = new Set(thread.messages.map((message) => message.id));\n for (const message of ctx.request.messages) {\n if (turnMessageIds.has(message.id)) {\n continue;\n }\n if (!existingIds.has(message.id)) {\n thread.push(message);\n existingIds.add(message.id);\n }\n }\n }\n\n thread.append(turn);\n\n try {\n await adapter.save(adapter.id, thread, turn);\n } catch (error) {\n const err = toError(error);\n throw new Error(`Persistence adapter failed to save thread \"${adapter.id}\": ${err.message}`, {\n cause: err,\n });\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { M as Middleware } from '../../llm-BWLaTzzY.js';
|
|
2
|
+
import { E as EventDelta, S as StreamEvent, T as Turn } from '../../stream-bBd_4Ipu.js';
|
|
3
|
+
import '../../tool-BmAfKNBq.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @fileoverview Pipeline middleware for post-turn processing stages.
|
|
7
|
+
*
|
|
8
|
+
* Enables running async tasks (image generation, embedding, slug generation, etc.)
|
|
9
|
+
* after the LLM completes, while streaming progress events to connected clients.
|
|
10
|
+
*
|
|
11
|
+
* @module middleware/pipeline
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Pipeline stage event delta with stage-specific data.
|
|
16
|
+
*/
|
|
17
|
+
interface PipelineStageDelta extends EventDelta {
|
|
18
|
+
/** Stage identifier */
|
|
19
|
+
stage: string;
|
|
20
|
+
/** Stage output payload */
|
|
21
|
+
payload: unknown;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Stream event for pipeline stage progress.
|
|
25
|
+
* Extends StreamEvent with pipeline-specific delta containing stage and payload.
|
|
26
|
+
*/
|
|
27
|
+
interface PipelineStageEvent extends StreamEvent {
|
|
28
|
+
type: 'pipeline_stage';
|
|
29
|
+
delta: PipelineStageDelta;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Creates a pipeline stage stream event.
|
|
33
|
+
*
|
|
34
|
+
* @param stage - The stage identifier
|
|
35
|
+
* @param payload - The stage output payload
|
|
36
|
+
* @returns A pipeline stage event
|
|
37
|
+
*/
|
|
38
|
+
declare function pipelineStageEvent(stage: string, payload: unknown): PipelineStageEvent;
|
|
39
|
+
/**
|
|
40
|
+
* Type guard for PipelineStageEvent.
|
|
41
|
+
*
|
|
42
|
+
* @param event - Stream event to check
|
|
43
|
+
* @returns True if the event is a PipelineStageEvent
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* for await (const event of stream) {
|
|
48
|
+
* if (isPipelineStageEvent(event)) {
|
|
49
|
+
* console.log(event.delta.stage, event.delta.payload);
|
|
50
|
+
* }
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
declare function isPipelineStageEvent(event: StreamEvent): event is PipelineStageEvent;
|
|
55
|
+
/**
|
|
56
|
+
* Emit function provided to pipeline stages.
|
|
57
|
+
*/
|
|
58
|
+
type PipelineEmit = (data: unknown) => void;
|
|
59
|
+
/**
|
|
60
|
+
* A single pipeline stage that runs after turn completion.
|
|
61
|
+
*
|
|
62
|
+
* Stages can mutate the turn object to attach computed properties (like slugs,
|
|
63
|
+
* image URLs, etc.) that will be available in the `.then()` callback. Use a
|
|
64
|
+
* type assertion to add properties:
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* // Define extended turn type
|
|
69
|
+
* interface ExtendedTurn { slug?: string; imageUrl?: string; }
|
|
70
|
+
*
|
|
71
|
+
* // In stage run function
|
|
72
|
+
* run: (turn, emit) => {
|
|
73
|
+
* const slug = generateSlug(turn.data!.title);
|
|
74
|
+
* (turn as Turn<TData> & ExtendedTurn).slug = slug;
|
|
75
|
+
* emit({ slug });
|
|
76
|
+
* }
|
|
77
|
+
*
|
|
78
|
+
* // Access in .then() callback
|
|
79
|
+
* model.stream(prompt).then(turn => {
|
|
80
|
+
* const extended = turn as typeof turn & ExtendedTurn;
|
|
81
|
+
* console.log(extended.slug); // Available!
|
|
82
|
+
* });
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* @typeParam TData - Type of the turn's structured data
|
|
86
|
+
*/
|
|
87
|
+
interface PipelineStage<TData = unknown> {
|
|
88
|
+
/** Unique identifier for this stage (used in events) */
|
|
89
|
+
type: string;
|
|
90
|
+
/**
|
|
91
|
+
* Execute this stage.
|
|
92
|
+
*
|
|
93
|
+
* @param turn - The completed turn (can mutate via type assertion to add properties)
|
|
94
|
+
* @param emit - Function to emit progress events to subscribers
|
|
95
|
+
*/
|
|
96
|
+
run: (turn: Turn<TData>, emit: PipelineEmit) => Promise<void> | void;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Stage error details passed to onStageError callback.
|
|
100
|
+
*/
|
|
101
|
+
interface PipelineStageError<TData = unknown> {
|
|
102
|
+
/** The stage that failed */
|
|
103
|
+
stage: PipelineStage<TData>;
|
|
104
|
+
/** The error that occurred */
|
|
105
|
+
error: Error;
|
|
106
|
+
/** The turn being processed */
|
|
107
|
+
turn: Turn<TData>;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Pipeline middleware configuration.
|
|
111
|
+
*
|
|
112
|
+
* @typeParam TData - Type of the turn's structured data
|
|
113
|
+
*/
|
|
114
|
+
interface PipelineConfig<TData = unknown> {
|
|
115
|
+
/** Stages to run after turn completion, executed in order */
|
|
116
|
+
stages: PipelineStage<TData>[];
|
|
117
|
+
/** Run stages in parallel instead of sequential (default: false) */
|
|
118
|
+
parallel?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Continue running subsequent stages even if one fails (default: false).
|
|
121
|
+
* In parallel mode, all stages run regardless; this only affects error propagation.
|
|
122
|
+
*/
|
|
123
|
+
continueOnError?: boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Called when a stage throws an error.
|
|
126
|
+
* Useful for logging or cleanup. Does not prevent error propagation unless continueOnError is true.
|
|
127
|
+
*/
|
|
128
|
+
onStageError?: (details: PipelineStageError<TData>) => void | Promise<void>;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Creates pipeline middleware for post-turn processing stages.
|
|
132
|
+
*
|
|
133
|
+
* Pipeline middleware enables running async tasks (image generation, embedding,
|
|
134
|
+
* slug generation, etc.) after the LLM completes, while streaming progress
|
|
135
|
+
* events to connected clients.
|
|
136
|
+
*
|
|
137
|
+
* Events are emitted through the standard middleware pipeline via `ctx.emit()`,
|
|
138
|
+
* so any middleware with `onStreamEvent` (like pubsub) will receive them.
|
|
139
|
+
*
|
|
140
|
+
* **Middleware Order**: Place pipeline AFTER pubsub in the array so that:
|
|
141
|
+
* - `onStart`: pubsub runs first (sets up adapter)
|
|
142
|
+
* - `onTurn`: pipeline runs first (emits events), pubsub runs second (cleans up)
|
|
143
|
+
*
|
|
144
|
+
* @typeParam TData - Type of the turn's structured data
|
|
145
|
+
* @param config - Pipeline middleware configuration
|
|
146
|
+
* @returns A middleware instance
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```typescript
|
|
150
|
+
* import { llm } from '@providerprotocol/ai';
|
|
151
|
+
* import { openai } from '@providerprotocol/ai/openai';
|
|
152
|
+
* import { pubsubMiddleware, memoryAdapter } from '@providerprotocol/ai/middleware/pubsub';
|
|
153
|
+
* import { pipelineMiddleware } from '@providerprotocol/ai/middleware/pipeline';
|
|
154
|
+
*
|
|
155
|
+
* const adapter = memoryAdapter();
|
|
156
|
+
*
|
|
157
|
+
* const model = llm({
|
|
158
|
+
* model: openai('gpt-4o'),
|
|
159
|
+
* structure: BlogPostSchema,
|
|
160
|
+
* middleware: [
|
|
161
|
+
* pubsubMiddleware({ adapter, streamId: postId }),
|
|
162
|
+
* pipelineMiddleware<BlogPost>({
|
|
163
|
+
* stages: [
|
|
164
|
+
* {
|
|
165
|
+
* type: 'slug',
|
|
166
|
+
* run: async (turn, emit) => {
|
|
167
|
+
* const slug = await generateSlug(turn.data!.title);
|
|
168
|
+
* (turn as { slug?: string }).slug = slug;
|
|
169
|
+
* emit({ slug });
|
|
170
|
+
* },
|
|
171
|
+
* },
|
|
172
|
+
* {
|
|
173
|
+
* type: 'embedding',
|
|
174
|
+
* run: async (turn, emit) => {
|
|
175
|
+
* await vectorize(turn.data!);
|
|
176
|
+
* emit({ embedded: true });
|
|
177
|
+
* },
|
|
178
|
+
* },
|
|
179
|
+
* ],
|
|
180
|
+
* }),
|
|
181
|
+
* ],
|
|
182
|
+
* });
|
|
183
|
+
*
|
|
184
|
+
* model.stream(prompt).then(turn => {
|
|
185
|
+
* const extended = turn as typeof turn & { slug?: string };
|
|
186
|
+
* await db.posts.update({ _id: id }, {
|
|
187
|
+
* ...turn.data,
|
|
188
|
+
* slug: extended.slug,
|
|
189
|
+
* });
|
|
190
|
+
* });
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
declare function pipelineMiddleware<TData = unknown>(config: PipelineConfig<TData>): Middleware;
|
|
194
|
+
|
|
195
|
+
export { type PipelineConfig, type PipelineEmit, type PipelineStage, type PipelineStageDelta, type PipelineStageError, type PipelineStageEvent, isPipelineStageEvent, pipelineMiddleware, pipelineStageEvent };
|