@providerprotocol/ai 0.0.15 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { M as ModelReference, P as ProviderConfig, L as LLMProvider, a as LLMHandler$1, E as EmbeddingHandler, I as ImageHandler, b as Provider } from './provider-Bi0nyNhA.js';
2
- export { B as BoundEmbeddingModel, g as BoundImageModel, e as EmbeddingProvider, c as ErrorCode, f as ImageProvider, K as KeyStrategy, d as Modality, R as RetryStrategy, U as UPPError } from './provider-Bi0nyNhA.js';
3
- export { D as DynamicKey, E as ExponentialBackoff, L as LinearBackoff, N as NoRetry, a as RetryAfterStrategy, R as RoundRobinKeys, T as TokenBucket, W as WeightedKeys } from './retry-BatS2hjD.js';
1
+ import { P as ProviderConfig, L as LLMProvider, E as EmbeddingInput, a as EmbeddingUsage, B as BoundEmbeddingModel, b as LLMHandler$1, c as EmbeddingHandler, I as ImageHandler, d as Provider } from './provider-D5MO3-pS.js';
2
+ export { i as BoundImageModel, g as EmbeddingProvider, j as EmbeddingRequest, k as EmbeddingResponse, l as EmbeddingVector, e as ErrorCode, h as ImageProvider, K as KeyStrategy, M as Modality, f as ModelReference, R as RetryStrategy, U as UPPError } from './provider-D5MO3-pS.js';
3
+ export { D as DynamicKey, E as ExponentialBackoff, L as LinearBackoff, N as NoRetry, a as RetryAfterStrategy, R as RoundRobinKeys, T as TokenBucket, W as WeightedKeys } from './retry-DZ4Sqmxp.js';
4
4
 
5
5
  /**
6
6
  * @fileoverview Content block types for multimodal messages.
@@ -1513,6 +1513,21 @@ declare class Thread {
1513
1513
  * @module types/llm
1514
1514
  */
1515
1515
 
1516
+ /**
1517
+ * Structural type for model input that accepts any ModelReference.
1518
+ * Uses structural typing to avoid generic variance issues with Provider generics.
1519
+ * The nested types use `unknown` to accept any provider parameter types.
1520
+ */
1521
+ type ModelInput = {
1522
+ readonly modelId: string;
1523
+ readonly provider: {
1524
+ readonly name: string;
1525
+ readonly version: string;
1526
+ readonly modalities: {
1527
+ llm?: unknown;
1528
+ };
1529
+ };
1530
+ };
1516
1531
  /**
1517
1532
  * LLM capabilities declare what a provider's API supports.
1518
1533
  *
@@ -1547,6 +1562,8 @@ interface LLMCapabilities {
1547
1562
  videoInput: boolean;
1548
1563
  /** Provider API supports audio input in messages */
1549
1564
  audioInput: boolean;
1565
+ /** Provider API supports image generation output (via modalities or built-in tools) */
1566
+ imageOutput?: boolean;
1550
1567
  }
1551
1568
  /**
1552
1569
  * Valid input types for inference.
@@ -1575,7 +1592,7 @@ type InferenceInput = string | Message | ContentBlock;
1575
1592
  */
1576
1593
  interface LLMOptions<TParams = unknown> {
1577
1594
  /** A model reference from a provider factory */
1578
- model: ModelReference<any>;
1595
+ model: ModelInput;
1579
1596
  /** Provider infrastructure configuration (optional - uses env vars if omitted) */
1580
1597
  config?: ProviderConfig;
1581
1598
  /** Model-specific parameters (temperature, max_tokens, etc.) */
@@ -1814,6 +1831,205 @@ interface LLMHandler<TParams = unknown> {
1814
1831
  */
1815
1832
  declare function llm<TParams = unknown>(options: LLMOptions<TParams>): LLMInstance<TParams>;
1816
1833
 
1834
+ /**
1835
+ * @fileoverview Embedding types for vector embedding generation.
1836
+ *
1837
+ * Defines the interfaces for configuring and executing embedding operations,
1838
+ * including options, instances, requests, responses, and streaming progress.
1839
+ *
1840
+ * @module types/embedding
1841
+ */
1842
+
1843
+ /**
1844
+ * Structural type for embedding model input.
1845
+ * Uses structural typing to avoid generic variance issues with Provider generics.
1846
+ */
1847
+ interface EmbeddingModelInput {
1848
+ readonly modelId: string;
1849
+ readonly provider: {
1850
+ readonly name: string;
1851
+ readonly version: string;
1852
+ readonly modalities: {
1853
+ embedding?: unknown;
1854
+ };
1855
+ };
1856
+ }
1857
+ /**
1858
+ * Options for creating an embedding instance with the embedding() function.
1859
+ *
1860
+ * @typeParam TParams - Provider-specific parameter type
1861
+ *
1862
+ * @example
1863
+ * ```typescript
1864
+ * const options: EmbeddingOptions<OpenAIEmbedParams> = {
1865
+ * model: openai('text-embedding-3-large'),
1866
+ * config: { apiKey: process.env.OPENAI_API_KEY },
1867
+ * params: { dimensions: 1536 }
1868
+ * };
1869
+ * ```
1870
+ */
1871
+ interface EmbeddingOptions<TParams = unknown> {
1872
+ /** A model reference from a provider factory */
1873
+ model: EmbeddingModelInput;
1874
+ /** Provider infrastructure configuration */
1875
+ config?: ProviderConfig;
1876
+ /** Provider-specific parameters (passed through unchanged) */
1877
+ params?: TParams;
1878
+ }
1879
+ /**
1880
+ * Options for embed() calls.
1881
+ */
1882
+ interface EmbedOptions {
1883
+ /**
1884
+ * Enable chunked processing with progress for large input sets.
1885
+ * When true, returns EmbeddingStream instead of Promise.
1886
+ */
1887
+ chunked?: boolean;
1888
+ /** Inputs per batch when chunked (default: provider max) */
1889
+ batchSize?: number;
1890
+ /** Concurrent batch limit when chunked (default: 1) */
1891
+ concurrency?: number;
1892
+ /** Abort signal for cancellation */
1893
+ signal?: AbortSignal;
1894
+ }
1895
+ /**
1896
+ * Single embedding vector result.
1897
+ */
1898
+ interface Embedding {
1899
+ /** The embedding vector */
1900
+ vector: number[];
1901
+ /** Vector dimensionality */
1902
+ dimensions: number;
1903
+ /** Index corresponding to input array position */
1904
+ index: number;
1905
+ /** Token count for this input (if provider reports) */
1906
+ tokens?: number;
1907
+ /** Provider-specific per-embedding metadata */
1908
+ metadata?: Record<string, unknown>;
1909
+ }
1910
+ /**
1911
+ * Result from embed() call.
1912
+ */
1913
+ interface EmbeddingResult {
1914
+ /** Embeddings in same order as inputs */
1915
+ embeddings: Embedding[];
1916
+ /** Usage statistics */
1917
+ usage: EmbeddingUsage;
1918
+ /** Provider-specific response metadata */
1919
+ metadata?: Record<string, unknown>;
1920
+ }
1921
+ /**
1922
+ * Progress update when using chunked mode.
1923
+ */
1924
+ interface EmbeddingProgress {
1925
+ /** Embeddings from the latest batch */
1926
+ embeddings: Embedding[];
1927
+ /** Total embeddings completed so far */
1928
+ completed: number;
1929
+ /** Total number of inputs */
1930
+ total: number;
1931
+ /** Percentage complete (0-100) */
1932
+ percent: number;
1933
+ }
1934
+ /**
1935
+ * Async iterable stream with final result accessor.
1936
+ * Returned when embed() is called with { chunked: true }.
1937
+ */
1938
+ interface EmbeddingStream extends AsyncIterable<EmbeddingProgress> {
1939
+ /** Promise resolving to complete result after iteration */
1940
+ readonly result: Promise<EmbeddingResult>;
1941
+ /** Abort the operation */
1942
+ abort(): void;
1943
+ }
1944
+ /**
1945
+ * Embedding instance returned by the embedding() function.
1946
+ *
1947
+ * @typeParam TParams - Provider-specific parameter type
1948
+ *
1949
+ * @example
1950
+ * ```typescript
1951
+ * const embedder = embedding({ model: openai('text-embedding-3-large') });
1952
+ *
1953
+ * // Single input
1954
+ * const result = await embedder.embed('Hello world');
1955
+ *
1956
+ * // Batch input
1957
+ * const batch = await embedder.embed(['doc1', 'doc2', 'doc3']);
1958
+ *
1959
+ * // Large-scale with progress
1960
+ * const stream = embedder.embed(documents, { chunked: true });
1961
+ * for await (const progress of stream) {
1962
+ * console.log(`${progress.percent}% complete`);
1963
+ * }
1964
+ * ```
1965
+ */
1966
+ interface EmbeddingInstance<TParams = unknown> {
1967
+ /**
1968
+ * Generate embeddings for one or more inputs.
1969
+ *
1970
+ * @param input - Single input or array of inputs
1971
+ * @param options - Optional embed options
1972
+ * @returns Promise<EmbeddingResult> or EmbeddingStream if chunked
1973
+ */
1974
+ embed(input: EmbeddingInput | EmbeddingInput[], options?: EmbedOptions & {
1975
+ chunked?: false;
1976
+ }): Promise<EmbeddingResult>;
1977
+ embed(input: EmbeddingInput[], options: EmbedOptions & {
1978
+ chunked: true;
1979
+ }): EmbeddingStream;
1980
+ embed(input: EmbeddingInput | EmbeddingInput[], options?: EmbedOptions): Promise<EmbeddingResult> | EmbeddingStream;
1981
+ /** The bound embedding model */
1982
+ readonly model: BoundEmbeddingModel<TParams>;
1983
+ /** Current parameters */
1984
+ readonly params: TParams | undefined;
1985
+ }
1986
+
1987
+ /**
1988
+ * @fileoverview Embedding instance factory for the Universal Provider Protocol.
1989
+ *
1990
+ * This module provides the core functionality for creating embedding instances
1991
+ * that generate vector embeddings from text or other content.
1992
+ *
1993
+ * @module core/embedding
1994
+ */
1995
+
1996
+ /**
1997
+ * Creates an embedding instance configured with the specified options.
1998
+ *
1999
+ * This is the primary factory function for creating embedding instances.
2000
+ * It validates provider capabilities, binds the model, and returns an
2001
+ * instance with an `embed` method for generating embeddings.
2002
+ *
2003
+ * @typeParam TParams - Provider-specific parameter type
2004
+ * @param options - Configuration options for the embedding instance
2005
+ * @returns A configured embedding instance ready for use
2006
+ * @throws {UPPError} When the provider does not support the embedding modality
2007
+ *
2008
+ * @example
2009
+ * ```typescript
2010
+ * import { embedding } from 'upp';
2011
+ * import { openai } from 'upp/openai';
2012
+ *
2013
+ * const embedder = embedding({
2014
+ * model: openai('text-embedding-3-large'),
2015
+ * params: { dimensions: 1536 }
2016
+ * });
2017
+ *
2018
+ * // Single input
2019
+ * const result = await embedder.embed('Hello world');
2020
+ *
2021
+ * // Batch input
2022
+ * const batch = await embedder.embed(['doc1', 'doc2', 'doc3']);
2023
+ *
2024
+ * // Large-scale with progress
2025
+ * const stream = embedder.embed(documents, { chunked: true });
2026
+ * for await (const progress of stream) {
2027
+ * console.log(`${progress.percent}% complete`);
2028
+ * }
2029
+ * ```
2030
+ */
2031
+ declare function embedding<TParams = unknown>(options: EmbeddingOptions<TParams>): EmbeddingInstance<TParams>;
2032
+
1817
2033
  /**
1818
2034
  * @fileoverview Base provider interface and factory for the Universal Provider Protocol.
1819
2035
  *
@@ -2091,6 +2307,8 @@ declare class Image {
2091
2307
  declare const ai: {
2092
2308
  /** LLM instance factory */
2093
2309
  llm: typeof llm;
2310
+ /** Embedding instance factory */
2311
+ embedding: typeof embedding;
2094
2312
  };
2095
2313
 
2096
- export { type AfterCallResult, type AssistantContent, AssistantMessage, type AudioBlock, type BeforeCallResult, type BinaryBlock, type BoundLLMModel, type ContentBlock, EmbeddingHandler, type EventDelta, Image, type ImageBlock, ImageHandler, type ImageSource, type InferenceInput, type JSONSchema, type JSONSchemaProperty, type JSONSchemaPropertyType, type LLMCapabilities, type LLMHandler, type LLMInstance, type LLMOptions, LLMProvider, type LLMRequest, type LLMResponse, type LLMStreamResult, Message, type MessageJSON, type MessageMetadata, type MessageOptions, type MessageType, ModelReference, Provider, ProviderConfig, type StreamEvent, type StreamEventType, type StreamResult, type TextBlock, Thread, type ThreadJSON, type TokenUsage, type Tool, type ToolCall, type ToolExecution, type ToolMetadata, type ToolResult, ToolResultMessage, type ToolUseStrategy, type Turn, type UserContent, UserMessage, type VideoBlock, aggregateUsage, ai, contentBlockStart, contentBlockStop, createProvider, createStreamResult, createTurn, emptyUsage, isAssistantMessage, isAudioBlock, isBinaryBlock, isImageBlock, isTextBlock, isToolResultMessage, isUserMessage, isVideoBlock, llm, messageStart, messageStop, text, textDelta, toolCallDelta };
2314
+ export { type AfterCallResult, type AssistantContent, AssistantMessage, type AudioBlock, type BeforeCallResult, type BinaryBlock, BoundEmbeddingModel, type BoundLLMModel, type ContentBlock, type EmbedOptions, type Embedding, EmbeddingHandler, EmbeddingInput, type EmbeddingInstance, type EmbeddingModelInput, type EmbeddingOptions, type EmbeddingProgress, type EmbeddingResult, type EmbeddingStream, EmbeddingUsage, type EventDelta, Image, type ImageBlock, ImageHandler, type ImageSource, type InferenceInput, type JSONSchema, type JSONSchemaProperty, type JSONSchemaPropertyType, type LLMCapabilities, type LLMHandler, type LLMInstance, type LLMOptions, LLMProvider, type LLMRequest, type LLMResponse, type LLMStreamResult, Message, type MessageJSON, type MessageMetadata, type MessageOptions, type MessageType, Provider, ProviderConfig, type StreamEvent, type StreamEventType, type StreamResult, type TextBlock, Thread, type ThreadJSON, type TokenUsage, type Tool, type ToolCall, type ToolExecution, type ToolMetadata, type ToolResult, ToolResultMessage, type ToolUseStrategy, type Turn, type UserContent, UserMessage, type VideoBlock, aggregateUsage, ai, contentBlockStart, contentBlockStop, createProvider, createStreamResult, createTurn, embedding, emptyUsage, isAssistantMessage, isAudioBlock, isBinaryBlock, isImageBlock, isTextBlock, isToolResultMessage, isUserMessage, isVideoBlock, llm, messageStart, messageStop, text, textDelta, toolCallDelta };
package/dist/index.js CHANGED
@@ -161,7 +161,8 @@ function llm(options) {
161
161
  "llm"
162
162
  );
163
163
  }
164
- const boundModel = provider.modalities.llm.bind(modelRef.modelId);
164
+ const llmHandler = provider.modalities.llm;
165
+ const boundModel = llmHandler.bind(modelRef.modelId);
165
166
  const capabilities = boundModel.capabilities;
166
167
  if (structure && !capabilities.structuredOutput) {
167
168
  throw new UPPError(
@@ -577,6 +578,152 @@ function validateMediaCapabilities(messages, capabilities, providerName) {
577
578
  }
578
579
  }
579
580
 
581
+ // src/core/embedding.ts
582
+ function embedding(options) {
583
+ const { model: modelRef, config = {}, params } = options;
584
+ const provider = modelRef.provider;
585
+ if (!provider.modalities.embedding) {
586
+ throw new UPPError(
587
+ `Provider '${provider.name}' does not support embedding modality`,
588
+ "INVALID_REQUEST",
589
+ provider.name,
590
+ "embedding"
591
+ );
592
+ }
593
+ const handler = provider.modalities.embedding;
594
+ const boundModel = handler.bind(modelRef.modelId);
595
+ const instance = {
596
+ model: boundModel,
597
+ params,
598
+ embed(input, embedOptions) {
599
+ const inputs = Array.isArray(input) ? input : [input];
600
+ if (embedOptions?.chunked) {
601
+ return createChunkedStream(boundModel, inputs, params, config, embedOptions);
602
+ }
603
+ return executeEmbed(boundModel, inputs, params, config, embedOptions?.signal);
604
+ }
605
+ };
606
+ return instance;
607
+ }
608
+ async function executeEmbed(model, inputs, params, config, signal) {
609
+ const response = await model.embed({
610
+ inputs,
611
+ params,
612
+ config: config ?? {},
613
+ signal
614
+ });
615
+ return normalizeResponse(response);
616
+ }
617
+ function normalizeResponse(response) {
618
+ return {
619
+ embeddings: response.embeddings.map((vec, i) => {
620
+ const vector = normalizeVector(vec.vector);
621
+ return {
622
+ vector,
623
+ dimensions: vector.length,
624
+ index: vec.index ?? i,
625
+ tokens: vec.tokens,
626
+ metadata: vec.metadata
627
+ };
628
+ }),
629
+ usage: response.usage,
630
+ metadata: response.metadata
631
+ };
632
+ }
633
+ function normalizeVector(vector) {
634
+ if (Array.isArray(vector)) {
635
+ return vector;
636
+ }
637
+ return decodeBase64(vector);
638
+ }
639
+ function decodeBase64(b64) {
640
+ const binary = atob(b64);
641
+ const bytes = new Uint8Array(binary.length);
642
+ for (let i = 0; i < binary.length; i++) {
643
+ bytes[i] = binary.charCodeAt(i);
644
+ }
645
+ const floats = new Float32Array(bytes.buffer);
646
+ return Array.from(floats);
647
+ }
648
+ function createChunkedStream(model, inputs, params, config, options) {
649
+ const abortController = new AbortController();
650
+ const batchSize = options.batchSize ?? model.maxBatchSize;
651
+ const concurrency = options.concurrency ?? 1;
652
+ let resolveResult;
653
+ let rejectResult;
654
+ const resultPromise = new Promise((resolve, reject) => {
655
+ resolveResult = resolve;
656
+ rejectResult = reject;
657
+ });
658
+ async function* generate() {
659
+ const total = inputs.length;
660
+ const allEmbeddings = [];
661
+ let totalTokens = 0;
662
+ const batches = [];
663
+ for (let i = 0; i < inputs.length; i += batchSize) {
664
+ batches.push(inputs.slice(i, i + batchSize));
665
+ }
666
+ try {
667
+ for (let i = 0; i < batches.length; i += concurrency) {
668
+ if (abortController.signal.aborted || options.signal?.aborted) {
669
+ throw new UPPError(
670
+ "Embedding cancelled",
671
+ "CANCELLED",
672
+ model.provider.name,
673
+ "embedding"
674
+ );
675
+ }
676
+ const chunk = batches.slice(i, i + concurrency);
677
+ const responses = await Promise.all(
678
+ chunk.map(
679
+ (batch) => model.embed({
680
+ inputs: batch,
681
+ params,
682
+ config: config ?? {},
683
+ signal: abortController.signal
684
+ })
685
+ )
686
+ );
687
+ const batchEmbeddings = [];
688
+ for (const response of responses) {
689
+ for (const vec of response.embeddings) {
690
+ const vector = normalizeVector(vec.vector);
691
+ const emb = {
692
+ vector,
693
+ dimensions: vector.length,
694
+ index: allEmbeddings.length + batchEmbeddings.length,
695
+ tokens: vec.tokens,
696
+ metadata: vec.metadata
697
+ };
698
+ batchEmbeddings.push(emb);
699
+ }
700
+ totalTokens += response.usage.totalTokens;
701
+ }
702
+ allEmbeddings.push(...batchEmbeddings);
703
+ yield {
704
+ embeddings: batchEmbeddings,
705
+ completed: allEmbeddings.length,
706
+ total,
707
+ percent: allEmbeddings.length / total * 100
708
+ };
709
+ }
710
+ resolveResult({
711
+ embeddings: allEmbeddings,
712
+ usage: { totalTokens }
713
+ });
714
+ } catch (error) {
715
+ rejectResult(error);
716
+ throw error;
717
+ }
718
+ }
719
+ const generator = generate();
720
+ return {
721
+ [Symbol.asyncIterator]: () => generator,
722
+ result: resultPromise,
723
+ abort: () => abortController.abort()
724
+ };
725
+ }
726
+
580
727
  // src/core/image.ts
581
728
  import { readFile } from "fs/promises";
582
729
  var Image = class _Image {
@@ -1073,7 +1220,9 @@ var Thread = class _Thread {
1073
1220
  // src/index.ts
1074
1221
  var ai = {
1075
1222
  /** LLM instance factory */
1076
- llm
1223
+ llm,
1224
+ /** Embedding instance factory */
1225
+ embedding
1077
1226
  };
1078
1227
  export {
1079
1228
  AssistantMessage,
@@ -1098,6 +1247,7 @@ export {
1098
1247
  createProvider,
1099
1248
  createStreamResult,
1100
1249
  createTurn,
1250
+ embedding,
1101
1251
  emptyUsage,
1102
1252
  isAssistantMessage,
1103
1253
  isAudioBlock,