@absolutejs/ai 0.0.41 → 0.0.42

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 CHANGED
@@ -4,3 +4,11 @@ Standalone AI runtime and provider package extracted from AbsoluteJS.
4
4
 
5
5
  This package currently focuses on generic AI/chat/provider functionality.
6
6
  RAG remains a separate package.
7
+
8
+ Provider traffic can cross a trusted control plane without reimplementing a
9
+ vendor protocol. `remoteProvider()` carries normalized provider parameters and
10
+ chunks over SSE, while `createProviderProxyResponse()` hosts any
11
+ `AIProviderConfig` with pre-first-token and inter-token heartbeats. Provider
12
+ callbacks and abort objects never cross the wire. The Anthropic provider also
13
+ accepts an injectable `fetch`, allowing hosts to retain egress policy, tracing,
14
+ and test transports.
package/dist/ai/index.js CHANGED
@@ -1956,7 +1956,8 @@ async function* parseSSEStream4(body, signal) {
1956
1956
  var fetchAndStream = async function* (baseUrl, config2, params, configuredMax, promptCaching) {
1957
1957
  const body = buildRequestBody4(params, configuredMax, promptCaching);
1958
1958
  const target = `${baseUrl}/v1/messages`;
1959
- const response = await fetch(target, {
1959
+ const fetchImpl = config2.fetch ?? fetch;
1960
+ const response = await fetchImpl(target, {
1960
1961
  ...h2IfHttps4(target),
1961
1962
  body: JSON.stringify(body),
1962
1963
  headers: {
@@ -3654,6 +3655,165 @@ var streamAIWithTools = async function* (options) {
3654
3655
  yield { ...summary, type: "done" };
3655
3656
  return summary;
3656
3657
  };
3658
+ // src/ai/providerProxy.ts
3659
+ var DEFAULT_HEARTBEAT_MS2 = 5000;
3660
+ var encoder = new TextEncoder;
3661
+ var wireParams = (params) => ({
3662
+ ...params.cacheSystemPrompt === undefined ? {} : { cacheSystemPrompt: params.cacheSystemPrompt },
3663
+ ...params.frequencyPenalty === undefined ? {} : { frequencyPenalty: params.frequencyPenalty },
3664
+ ...params.maxTokens === undefined ? {} : { maxTokens: params.maxTokens },
3665
+ messages: params.messages,
3666
+ model: params.model,
3667
+ ...params.parallelToolCalls === undefined ? {} : { parallelToolCalls: params.parallelToolCalls },
3668
+ ...params.presencePenalty === undefined ? {} : { presencePenalty: params.presencePenalty },
3669
+ ...params.promptCaching === undefined ? {} : { promptCaching: params.promptCaching },
3670
+ ...params.reasoning === undefined ? {} : { reasoning: params.reasoning },
3671
+ ...params.responseFormat === undefined ? {} : { responseFormat: params.responseFormat },
3672
+ ...params.seed === undefined ? {} : { seed: params.seed },
3673
+ ...params.stopSequences === undefined ? {} : { stopSequences: params.stopSequences },
3674
+ ...params.systemPrompt === undefined ? {} : { systemPrompt: params.systemPrompt },
3675
+ ...params.temperature === undefined ? {} : { temperature: params.temperature },
3676
+ ...params.toolChoice === undefined ? {} : { toolChoice: params.toolChoice },
3677
+ ...params.tools === undefined ? {} : { tools: params.tools },
3678
+ ...params.topP === undefined ? {} : { topP: params.topP }
3679
+ });
3680
+ var parseProviderProxyParams = (value) => {
3681
+ if (!value || typeof value !== "object" || Array.isArray(value))
3682
+ return null;
3683
+ const input = value;
3684
+ if (typeof input.model !== "string" || input.model.trim() === "")
3685
+ return null;
3686
+ if (!Array.isArray(input.messages))
3687
+ return null;
3688
+ return wireParams(input);
3689
+ };
3690
+ var encodeEvent = (event, data) => encoder.encode(`event: ${event}
3691
+ data: ${JSON.stringify(data)}
3692
+
3693
+ `);
3694
+ var errorPayload = (error) => {
3695
+ const providerError = error instanceof ProviderError ? error : ProviderError.from(error, "remote");
3696
+ return {
3697
+ message: providerError.message,
3698
+ provider: providerError.provider,
3699
+ retryable: providerError.retryable,
3700
+ status: providerError.status,
3701
+ type: providerError.type
3702
+ };
3703
+ };
3704
+ var streamResponseBody = (iterator, heartbeatMs, onError) => new ReadableStream({
3705
+ async start(controller) {
3706
+ try {
3707
+ for (;; ) {
3708
+ const pending = iterator.next();
3709
+ let next;
3710
+ for (;; ) {
3711
+ let timer;
3712
+ const heartbeat = new Promise((resolve) => {
3713
+ timer = setTimeout(() => resolve("heartbeat"), heartbeatMs);
3714
+ });
3715
+ const winner = heartbeatMs > 0 ? await Promise.race([pending, heartbeat]) : await pending;
3716
+ if (timer)
3717
+ clearTimeout(timer);
3718
+ if (winner === "heartbeat") {
3719
+ controller.enqueue(encoder.encode(`: ping
3720
+
3721
+ `));
3722
+ continue;
3723
+ }
3724
+ next = winner;
3725
+ break;
3726
+ }
3727
+ if (next.done)
3728
+ break;
3729
+ controller.enqueue(encodeEvent("chunk", next.value));
3730
+ }
3731
+ } catch (error) {
3732
+ await onError?.(error);
3733
+ controller.enqueue(encodeEvent("error", errorPayload(error)));
3734
+ } finally {
3735
+ await iterator.return?.();
3736
+ controller.close();
3737
+ }
3738
+ }
3739
+ });
3740
+ var createProviderProxyResponse = async (provider, value, options = {}) => {
3741
+ const params = parseProviderProxyParams(value);
3742
+ if (!params) {
3743
+ return Response.json({ error: "invalid provider stream request" }, { status: 400 });
3744
+ }
3745
+ const iterator = provider.stream({ ...params, signal: options.signal })[Symbol.asyncIterator]();
3746
+ return new Response(streamResponseBody(iterator, options.heartbeatMs ?? DEFAULT_HEARTBEAT_MS2, options.onError), {
3747
+ headers: {
3748
+ "cache-control": "no-cache",
3749
+ "content-type": "text/event-stream; charset=utf-8",
3750
+ "x-accel-buffering": "no",
3751
+ ...Object.fromEntries(new Headers(options.headers))
3752
+ }
3753
+ });
3754
+ };
3755
+ var parseRemoteStream = async function* (response) {
3756
+ if (!response.ok) {
3757
+ throw ProviderError.fromResponse("remote", response.status, await response.text());
3758
+ }
3759
+ if (!response.body)
3760
+ throw new ProviderError({
3761
+ message: "Remote provider returned no response body",
3762
+ provider: "remote",
3763
+ retryable: true
3764
+ });
3765
+ const reader = response.body.getReader();
3766
+ const decoder = new TextDecoder;
3767
+ let buffer = "";
3768
+ try {
3769
+ for (;; ) {
3770
+ const { done, value } = await reader.read();
3771
+ buffer += decoder.decode(value, { stream: !done });
3772
+ const frames = buffer.split(/\r?\n\r?\n/);
3773
+ buffer = frames.pop() ?? "";
3774
+ for (const frame of frames) {
3775
+ if (frame.startsWith(":"))
3776
+ continue;
3777
+ const event = /^event:\s*(.+)$/m.exec(frame)?.[1];
3778
+ const data = frame.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join(`
3779
+ `);
3780
+ if (!data)
3781
+ continue;
3782
+ const parsed = JSON.parse(data);
3783
+ if (event === "error") {
3784
+ throw new ProviderError({
3785
+ message: "message" in parsed && typeof parsed.message === "string" ? parsed.message : "Remote provider stream failed",
3786
+ provider: "provider" in parsed && typeof parsed.provider === "string" ? parsed.provider : "remote",
3787
+ retryable: "retryable" in parsed && typeof parsed.retryable === "boolean" ? parsed.retryable : true,
3788
+ status: "status" in parsed && (typeof parsed.status === "number" || parsed.status === null) ? parsed.status : null,
3789
+ type: "type" in parsed && (typeof parsed.type === "string" || parsed.type === null) ? parsed.type : null
3790
+ });
3791
+ }
3792
+ if (event === "chunk")
3793
+ yield parsed;
3794
+ }
3795
+ if (done)
3796
+ break;
3797
+ }
3798
+ } finally {
3799
+ reader.releaseLock();
3800
+ }
3801
+ };
3802
+ var remoteProvider = (config2) => ({
3803
+ stream: async function* (params) {
3804
+ const headers = typeof config2.headers === "function" ? await config2.headers() : config2.headers;
3805
+ const response = await (config2.fetch ?? fetch)(config2.url, {
3806
+ body: JSON.stringify(wireParams(params)),
3807
+ headers: {
3808
+ "content-type": "application/json",
3809
+ ...Object.fromEntries(new Headers(headers))
3810
+ },
3811
+ method: "POST",
3812
+ signal: params.signal
3813
+ });
3814
+ yield* parseRemoteStream(response);
3815
+ }
3816
+ });
3657
3817
  // src/ai/ui/uiCards.ts
3658
3818
  var createUiCards = (definitions) => {
3659
3819
  const byName = new Map(definitions.map((definition) => [definition.name, definition]));
@@ -5680,11 +5840,13 @@ export {
5680
5840
  serializeAIMessage,
5681
5841
  resolveRenderers,
5682
5842
  renderChartSvg,
5843
+ remoteProvider,
5683
5844
  providerStatusPage,
5684
5845
  planCard,
5685
5846
  parseUiActions,
5686
5847
  parseTableSpec,
5687
5848
  parseStatTilesSpec,
5849
+ parseProviderProxyParams,
5688
5850
  parsePlanSpec,
5689
5851
  parseFormSpec,
5690
5852
  parseDiffSpec,
@@ -5714,6 +5876,7 @@ export {
5714
5876
  credentialCard,
5715
5877
  createUiCards,
5716
5878
  createSyncConversationStore,
5879
+ createProviderProxyResponse,
5717
5880
  createOAuth2ClientCredentialsTokenSource,
5718
5881
  createMemoryStore,
5719
5882
  createConversationManager,
@@ -5751,5 +5914,5 @@ export {
5751
5914
  BUILTIN_UI_CARDS
5752
5915
  };
5753
5916
 
5754
- //# debugId=75AACEF004EE3AAE64756E2164756E21
5917
+ //# debugId=31B8793E47976E7B64756E2164756E21
5755
5918
  //# sourceMappingURL=index.js.map