@happyvertical/ai 0.78.2 → 0.79.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +63 -5
  2. package/dist/chunks/{anthropic-BRwbhwIl.js → anthropic-2z-82zgr.js} +75 -8
  3. package/dist/chunks/anthropic-2z-82zgr.js.map +1 -0
  4. package/dist/chunks/{bedrock-Cf1xUerN.js → bedrock-Dc2eVPUD.js} +153 -41
  5. package/dist/chunks/bedrock-Dc2eVPUD.js.map +1 -0
  6. package/dist/chunks/{bifrost-3mXtQsTj.js → bifrost-CEnCsciy.js} +36 -11
  7. package/dist/chunks/bifrost-CEnCsciy.js.map +1 -0
  8. package/dist/chunks/{claude-cli-BrHRfkry.js → claude-cli-X1ONjE8K.js} +3 -3
  9. package/dist/chunks/{claude-cli-BrHRfkry.js.map → claude-cli-X1ONjE8K.js.map} +1 -1
  10. package/dist/chunks/{gateway-admin-C4GFPbZF.js → gateway-admin-BUhBzXZb.js} +2 -2
  11. package/dist/chunks/{gateway-admin-C4GFPbZF.js.map → gateway-admin-BUhBzXZb.js.map} +1 -1
  12. package/dist/chunks/{gemini-BfpHXDIQ.js → gemini-CS56gY0D.js} +138 -32
  13. package/dist/chunks/gemini-CS56gY0D.js.map +1 -0
  14. package/dist/chunks/{huggingface-280qv9iv.js → huggingface-wQSfO5xA.js} +64 -19
  15. package/dist/chunks/huggingface-wQSfO5xA.js.map +1 -0
  16. package/dist/chunks/{index-BT4thAvS.js → index-DCXO0nZA.js} +383 -24
  17. package/dist/chunks/index-DCXO0nZA.js.map +1 -0
  18. package/dist/chunks/{litellm-DhPKa_Jz.js → litellm-BMFTYbWc.js} +36 -11
  19. package/dist/chunks/litellm-BMFTYbWc.js.map +1 -0
  20. package/dist/chunks/{ollama-Di1ldur0.js → ollama-DsGDrA-c.js} +171 -60
  21. package/dist/chunks/ollama-DsGDrA-c.js.map +1 -0
  22. package/dist/chunks/{openai-5snI2diE.js → openai-CJEo69jb.js} +145 -12
  23. package/dist/chunks/openai-CJEo69jb.js.map +1 -0
  24. package/dist/chunks/{qwen-tts-DgPgdXxG.js → qwen-tts-BLYZ6d9s.js} +2 -2
  25. package/dist/chunks/{qwen-tts-DgPgdXxG.js.map → qwen-tts-BLYZ6d9s.js.map} +1 -1
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +15 -12
  29. package/dist/shared/factory.d.ts.map +1 -1
  30. package/dist/shared/providers/anthropic.d.ts.map +1 -1
  31. package/dist/shared/providers/bedrock.d.ts.map +1 -1
  32. package/dist/shared/providers/bifrost.d.ts.map +1 -1
  33. package/dist/shared/providers/gemini.d.ts +0 -5
  34. package/dist/shared/providers/gemini.d.ts.map +1 -1
  35. package/dist/shared/providers/huggingface.d.ts.map +1 -1
  36. package/dist/shared/providers/litellm.d.ts.map +1 -1
  37. package/dist/shared/providers/ollama.d.ts +0 -1
  38. package/dist/shared/providers/ollama.d.ts.map +1 -1
  39. package/dist/shared/providers/openai.d.ts +2 -2
  40. package/dist/shared/providers/openai.d.ts.map +1 -1
  41. package/dist/shared/safety.d.ts +55 -0
  42. package/dist/shared/safety.d.ts.map +1 -0
  43. package/dist/shared/types.d.ts +74 -6
  44. package/dist/shared/types.d.ts.map +1 -1
  45. package/package.json +2 -2
  46. package/dist/chunks/anthropic-BRwbhwIl.js.map +0 -1
  47. package/dist/chunks/bedrock-Cf1xUerN.js.map +0 -1
  48. package/dist/chunks/bifrost-3mXtQsTj.js.map +0 -1
  49. package/dist/chunks/gemini-BfpHXDIQ.js.map +0 -1
  50. package/dist/chunks/huggingface-280qv9iv.js.map +0 -1
  51. package/dist/chunks/index-BT4thAvS.js.map +0 -1
  52. package/dist/chunks/litellm-DhPKa_Jz.js.map +0 -1
  53. package/dist/chunks/ollama-Di1ldur0.js.map +0 -1
  54. package/dist/chunks/openai-5snI2diE.js.map +0 -1
@@ -469,7 +469,7 @@ function normalizeRateLimitConfig(options) {
469
469
  key: rateLimit?.key?.trim() || deriveBudgetKey(options),
470
470
  maxAttempts: Math.max(
471
471
  1,
472
- normalizeNonNegativeInteger(rateLimit?.maxAttempts, 3)
472
+ normalizeNonNegativeInteger(rateLimit?.maxAttempts, 1)
473
473
  )
474
474
  };
475
475
  }
@@ -589,6 +589,357 @@ function createRateLimitedAI(client, options) {
589
589
  }
590
590
  });
591
591
  }
592
+ const DEFAULT_AI_TIMEOUT_MS = 12e4;
593
+ const DEFAULT_AI_MAX_RETRIES = 0;
594
+ const DEFAULT_AI_GENERATION_LIMITS = Object.freeze({
595
+ maxOutputTokens: 4096,
596
+ maxReasoningTokens: 1024,
597
+ maxImagesPerRequest: 1,
598
+ onExceeded: "error"
599
+ });
600
+ function positiveInteger(value, fallback, field) {
601
+ if (value === void 0) return fallback;
602
+ if (!Number.isFinite(value) || value <= 0) {
603
+ throw new AIError(
604
+ `${field} must be a positive finite number`,
605
+ "AI_LIMIT_INVALID"
606
+ );
607
+ }
608
+ return Math.trunc(value);
609
+ }
610
+ function normalizeBaseAIOptions(options) {
611
+ const configuredLimits = options.generationLimits || {};
612
+ const generationLimits = {
613
+ maxOutputTokens: positiveInteger(
614
+ configuredLimits.maxOutputTokens,
615
+ DEFAULT_AI_GENERATION_LIMITS.maxOutputTokens,
616
+ "generationLimits.maxOutputTokens"
617
+ ),
618
+ maxReasoningTokens: positiveInteger(
619
+ configuredLimits.maxReasoningTokens,
620
+ DEFAULT_AI_GENERATION_LIMITS.maxReasoningTokens,
621
+ "generationLimits.maxReasoningTokens"
622
+ ),
623
+ maxImagesPerRequest: positiveInteger(
624
+ configuredLimits.maxImagesPerRequest,
625
+ DEFAULT_AI_GENERATION_LIMITS.maxImagesPerRequest,
626
+ "generationLimits.maxImagesPerRequest"
627
+ ),
628
+ onExceeded: configuredLimits.onExceeded || DEFAULT_AI_GENERATION_LIMITS.onExceeded
629
+ };
630
+ if (!["error", "clamp"].includes(generationLimits.onExceeded)) {
631
+ throw new AIError(
632
+ 'generationLimits.onExceeded must be "error" or "clamp"',
633
+ "AI_LIMIT_INVALID"
634
+ );
635
+ }
636
+ return {
637
+ ...options,
638
+ timeout: positiveInteger(options.timeout, DEFAULT_AI_TIMEOUT_MS, "timeout"),
639
+ maxRetries: (() => {
640
+ if (options.maxRetries === void 0) return DEFAULT_AI_MAX_RETRIES;
641
+ if (!Number.isFinite(options.maxRetries) || options.maxRetries < 0) {
642
+ throw new AIError(
643
+ "maxRetries must be a non-negative finite number",
644
+ "AI_LIMIT_INVALID"
645
+ );
646
+ }
647
+ return Math.trunc(options.maxRetries);
648
+ })(),
649
+ generationLimits
650
+ };
651
+ }
652
+ function reasoningTokensForEffort(effort) {
653
+ switch (effort) {
654
+ case false:
655
+ case "none":
656
+ return 0;
657
+ case "minimal":
658
+ return 1024;
659
+ case "low":
660
+ return 2048;
661
+ case "medium":
662
+ return 4096;
663
+ case "high":
664
+ return 8192;
665
+ default:
666
+ return void 0;
667
+ }
668
+ }
669
+ function enforceLimit(requested, limit, behavior, label, provider, model) {
670
+ if (!Number.isFinite(requested) || requested < 0) {
671
+ throw new AIError(
672
+ `${label} must be a non-negative finite number`,
673
+ "AI_LIMIT_INVALID",
674
+ provider,
675
+ model
676
+ );
677
+ }
678
+ const normalized = Math.trunc(requested);
679
+ if (normalized <= limit) return normalized;
680
+ if (behavior === "clamp") return limit;
681
+ throw new AIError(
682
+ `${label} ${normalized} exceeds configured ceiling ${limit}`,
683
+ "AI_LIMIT_EXCEEDED",
684
+ provider,
685
+ model
686
+ );
687
+ }
688
+ function normalizeChatOptions(providerOptions, options, provider, model) {
689
+ const normalizedProvider = normalizeBaseAIOptions(providerOptions);
690
+ const limits = normalizedProvider.generationLimits;
691
+ const requestedOutput = options.maxTokens ?? limits.maxOutputTokens;
692
+ const legacyEffort = options.thinkingLevel ?? providerOptions.thinkingLevel;
693
+ const reasoningWasRequested = options.reasoning !== void 0 || legacyEffort !== void 0 || options.includeThoughts === true;
694
+ const requestedReasoning = reasoningWasRequested ? options.reasoning?.maxTokens ?? reasoningTokensForEffort(options.reasoning?.effort ?? legacyEffort) ?? limits.maxReasoningTokens : 0;
695
+ return {
696
+ ...options,
697
+ maxTokens: enforceLimit(
698
+ requestedOutput,
699
+ limits.maxOutputTokens,
700
+ limits.onExceeded,
701
+ "maxTokens",
702
+ provider,
703
+ model
704
+ ),
705
+ reasoning: {
706
+ ...options.reasoning,
707
+ maxTokens: enforceLimit(
708
+ requestedReasoning,
709
+ limits.maxReasoningTokens,
710
+ limits.onExceeded,
711
+ "reasoning.maxTokens",
712
+ provider,
713
+ model
714
+ ),
715
+ includeThoughts: options.reasoning?.includeThoughts ?? options.includeThoughts
716
+ }
717
+ };
718
+ }
719
+ function normalizeImageGenerationOptions(providerOptions, options, provider, model) {
720
+ const normalizedProvider = normalizeBaseAIOptions(providerOptions);
721
+ const limits = normalizedProvider.generationLimits;
722
+ return {
723
+ ...options,
724
+ n: enforceLimit(
725
+ options.n ?? 1,
726
+ limits.maxImagesPerRequest,
727
+ limits.onExceeded,
728
+ "n",
729
+ provider,
730
+ model
731
+ )
732
+ };
733
+ }
734
+ function prepareRequestControls(providerOptions, requestOptions = {}) {
735
+ const normalized = normalizeBaseAIOptions(providerOptions);
736
+ const timeout = positiveInteger(
737
+ requestOptions.timeout,
738
+ normalized.timeout,
739
+ "timeout"
740
+ );
741
+ const controller = new AbortController();
742
+ let timedOut = false;
743
+ const abortFromCaller = () => controller.abort(requestOptions.signal?.reason);
744
+ if (requestOptions.signal?.aborted) {
745
+ abortFromCaller();
746
+ } else {
747
+ requestOptions.signal?.addEventListener("abort", abortFromCaller, {
748
+ once: true
749
+ });
750
+ }
751
+ const timer = setTimeout(() => {
752
+ timedOut = true;
753
+ controller.abort(new Error(`AI request timed out after ${timeout}ms`));
754
+ }, timeout);
755
+ return {
756
+ signal: controller.signal,
757
+ timeout,
758
+ didTimeout: () => timedOut,
759
+ cleanup: () => {
760
+ clearTimeout(timer);
761
+ requestOptions.signal?.removeEventListener("abort", abortFromCaller);
762
+ }
763
+ };
764
+ }
765
+ function mergeTags(providerOptions, callTags) {
766
+ return providerOptions.usageTags || callTags ? { ...providerOptions.usageTags, ...callTags } : void 0;
767
+ }
768
+ function emitRequestEvent(providerOptions, event) {
769
+ if (!providerOptions.onRequest) return;
770
+ try {
771
+ providerOptions.onRequest({
772
+ ...event,
773
+ tags: mergeTags(providerOptions, event.tags)
774
+ });
775
+ } catch {
776
+ }
777
+ }
778
+ function classifyRequestFailure(error, didTimeout = false) {
779
+ const code = error instanceof AIError ? error.code : void 0;
780
+ if (code === "AI_LIMIT_EXCEEDED" || code === "AI_LIMIT_INVALID") {
781
+ return { status: "rejected", errorCode: code };
782
+ }
783
+ if (didTimeout || code === "AI_TIMEOUT" || code === "TIMEOUT") {
784
+ return { status: "timed_out", errorCode: code || "AI_TIMEOUT" };
785
+ }
786
+ if (error instanceof Error && error.name === "AbortError") {
787
+ return { status: "aborted", errorCode: "ABORTED" };
788
+ }
789
+ if (code === "AI_ABORTED") {
790
+ return { status: "aborted", errorCode: code };
791
+ }
792
+ return {
793
+ status: "failed",
794
+ errorCode: code || (error instanceof Error ? error.name : void 0)
795
+ };
796
+ }
797
+ function requestEventBase(options) {
798
+ const normalized = normalizeBaseAIOptions(options.providerOptions);
799
+ return {
800
+ provider: options.provider,
801
+ model: options.model,
802
+ operation: options.operation,
803
+ attempts: normalized.maxRetries + 1,
804
+ requestedMaxOutputTokens: options.callOptions?.maxTokens,
805
+ effectiveMaxOutputTokens: options.operation === "generateImage" ? void 0 : options.effectiveMaxOutputTokens ?? normalized.generationLimits.maxOutputTokens,
806
+ tags: options.callOptions?.usageTags
807
+ };
808
+ }
809
+ const OBSERVED_OPERATIONS = /* @__PURE__ */ new Set([
810
+ "chat",
811
+ "complete",
812
+ "message",
813
+ "describeImage",
814
+ "generateImage",
815
+ "stream"
816
+ ]);
817
+ function callOptionsFor(operation, args) {
818
+ const index = operation === "describeImage" ? 2 : 1;
819
+ const value = args[index];
820
+ return value && typeof value === "object" ? value : void 0;
821
+ }
822
+ function providerName(options) {
823
+ const configured = options.type;
824
+ return configured || "openai";
825
+ }
826
+ function effectiveOutputTokens(operation, providerOptions, callOptions) {
827
+ if (operation === "generateImage") {
828
+ normalizeImageGenerationOptions(
829
+ providerOptions,
830
+ callOptions || {},
831
+ providerName(providerOptions),
832
+ callOptions?.model
833
+ );
834
+ return void 0;
835
+ }
836
+ return normalizeChatOptions(
837
+ providerOptions,
838
+ callOptions || {},
839
+ providerName(providerOptions),
840
+ callOptions?.model || providerOptions.defaultModel
841
+ ).maxTokens;
842
+ }
843
+ function createObservedAI(client, providerOptions) {
844
+ if (!providerOptions.onRequest) return client;
845
+ return new Proxy(client, {
846
+ get(target, property, receiver) {
847
+ const value = Reflect.get(target, property, receiver);
848
+ if (typeof property !== "string" || typeof value !== "function" || !OBSERVED_OPERATIONS.has(property)) {
849
+ return typeof value === "function" ? value.bind(target) : value;
850
+ }
851
+ const operation = property;
852
+ if (operation === "stream") {
853
+ return async function* (...args) {
854
+ const startedAt = Date.now();
855
+ const callOptions = callOptionsFor(operation, args);
856
+ let base;
857
+ try {
858
+ base = requestEventBase({
859
+ providerOptions,
860
+ provider: providerName(providerOptions),
861
+ model: callOptions?.model || providerOptions.defaultModel || "unknown",
862
+ operation,
863
+ callOptions,
864
+ effectiveMaxOutputTokens: effectiveOutputTokens(
865
+ operation,
866
+ providerOptions,
867
+ callOptions
868
+ )
869
+ });
870
+ const iterable = Reflect.apply(
871
+ value,
872
+ target,
873
+ args
874
+ );
875
+ for await (const chunk of iterable) yield chunk;
876
+ emitRequestEvent(providerOptions, {
877
+ ...base,
878
+ status: "succeeded",
879
+ duration: Date.now() - startedAt
880
+ });
881
+ } catch (error) {
882
+ const failure = classifyRequestFailure(error);
883
+ base ||= requestEventBase({
884
+ providerOptions,
885
+ provider: providerName(providerOptions),
886
+ model: callOptions?.model || providerOptions.defaultModel || "unknown",
887
+ operation,
888
+ callOptions
889
+ });
890
+ emitRequestEvent(providerOptions, {
891
+ ...base,
892
+ ...failure,
893
+ duration: Date.now() - startedAt
894
+ });
895
+ throw error;
896
+ }
897
+ };
898
+ }
899
+ return async (...args) => {
900
+ const startedAt = Date.now();
901
+ const callOptions = callOptionsFor(operation, args);
902
+ let base;
903
+ try {
904
+ base = requestEventBase({
905
+ providerOptions,
906
+ provider: providerName(providerOptions),
907
+ model: callOptions?.model || providerOptions.defaultModel || "unknown",
908
+ operation,
909
+ callOptions,
910
+ effectiveMaxOutputTokens: effectiveOutputTokens(
911
+ operation,
912
+ providerOptions,
913
+ callOptions
914
+ )
915
+ });
916
+ const result = await Reflect.apply(value, target, args);
917
+ emitRequestEvent(providerOptions, {
918
+ ...base,
919
+ status: "succeeded",
920
+ duration: Date.now() - startedAt
921
+ });
922
+ return result;
923
+ } catch (error) {
924
+ const failure = classifyRequestFailure(error);
925
+ base ||= requestEventBase({
926
+ providerOptions,
927
+ provider: providerName(providerOptions),
928
+ model: callOptions?.model || providerOptions.defaultModel || "unknown",
929
+ operation,
930
+ callOptions
931
+ });
932
+ emitRequestEvent(providerOptions, {
933
+ ...base,
934
+ ...failure,
935
+ duration: Date.now() - startedAt
936
+ });
937
+ throw error;
938
+ }
939
+ };
940
+ }
941
+ });
942
+ }
592
943
  function isOpenAIOptions(options) {
593
944
  return !options.type || options.type === "openai";
594
945
  }
@@ -646,36 +997,37 @@ async function getAI(options = {}) {
646
997
  if ("model" in options && !options.defaultModel) {
647
998
  options.defaultModel = options.model;
648
999
  }
1000
+ options = normalizeBaseAIOptions(options);
649
1001
  let client;
650
1002
  if (isOpenAIOptions(options)) {
651
- const { OpenAIProvider } = await import("./openai-5snI2diE.js");
1003
+ const { OpenAIProvider } = await import("./openai-CJEo69jb.js");
652
1004
  client = new OpenAIProvider(options);
653
1005
  } else if (isLiteLLMOptions(options)) {
654
- const { LiteLLMProvider } = await import("./litellm-DhPKa_Jz.js");
1006
+ const { LiteLLMProvider } = await import("./litellm-BMFTYbWc.js");
655
1007
  client = new LiteLLMProvider(options);
656
1008
  } else if (isBifrostOptions(options)) {
657
- const { BifrostProvider } = await import("./bifrost-3mXtQsTj.js");
1009
+ const { BifrostProvider } = await import("./bifrost-CEnCsciy.js");
658
1010
  client = new BifrostProvider(options);
659
1011
  } else if (isOllamaOptions(options)) {
660
- const { OllamaProvider } = await import("./ollama-Di1ldur0.js");
1012
+ const { OllamaProvider } = await import("./ollama-DsGDrA-c.js");
661
1013
  client = new OllamaProvider(options);
662
1014
  } else if (isGeminiOptions(options)) {
663
- const { GeminiProvider } = await import("./gemini-BfpHXDIQ.js");
1015
+ const { GeminiProvider } = await import("./gemini-CS56gY0D.js");
664
1016
  client = new GeminiProvider(options);
665
1017
  } else if (isAnthropicOptions(options)) {
666
- const { AnthropicProvider } = await import("./anthropic-BRwbhwIl.js");
1018
+ const { AnthropicProvider } = await import("./anthropic-2z-82zgr.js");
667
1019
  client = new AnthropicProvider(options);
668
1020
  } else if (isHuggingFaceOptions(options)) {
669
- const { HuggingFaceProvider } = await import("./huggingface-280qv9iv.js");
1021
+ const { HuggingFaceProvider } = await import("./huggingface-wQSfO5xA.js");
670
1022
  client = new HuggingFaceProvider(options);
671
1023
  } else if (isBedrockOptions(options)) {
672
- const { BedrockProvider } = await import("./bedrock-Cf1xUerN.js");
1024
+ const { BedrockProvider } = await import("./bedrock-Dc2eVPUD.js");
673
1025
  client = new BedrockProvider(options);
674
1026
  } else if (isClaudeCliOptions(options)) {
675
- const { ClaudeCliProvider } = await import("./claude-cli-BrHRfkry.js");
1027
+ const { ClaudeCliProvider } = await import("./claude-cli-X1ONjE8K.js");
676
1028
  client = new ClaudeCliProvider(options);
677
1029
  } else if (isQwen3TTSOptions(options)) {
678
- const { Qwen3TTSProvider } = await import("./qwen-tts-DgPgdXxG.js");
1030
+ const { Qwen3TTSProvider } = await import("./qwen-tts-BLYZ6d9s.js");
679
1031
  client = new Qwen3TTSProvider(options);
680
1032
  } else {
681
1033
  throw new ValidationError("Unsupported AI provider type", {
@@ -683,7 +1035,7 @@ async function getAI(options = {}) {
683
1035
  providedType: options.type
684
1036
  });
685
1037
  }
686
- return createRateLimitedAI(client, options);
1038
+ return createObservedAI(createRateLimitedAI(client, options), options);
687
1039
  }
688
1040
  async function getAIAuto(options) {
689
1041
  const baseUrl = String(options.baseUrl || "");
@@ -914,21 +1266,28 @@ const PACKAGE_VERSION_INITIALIZED = true;
914
1266
  export {
915
1267
  AIError as A,
916
1268
  ContentFilterError as C,
1269
+ DEFAULT_AI_GENERATION_LIMITS as D,
917
1270
  ModelNotFoundError as M,
918
1271
  OpenAIClient as O,
919
1272
  PACKAGE_VERSION_INITIALIZED as P,
920
1273
  RateLimitError as R,
921
- ContextLengthError as a,
922
- AuthenticationError as b,
923
- extractTextContent as c,
924
- AIClient as d,
1274
+ normalizeImageGenerationOptions as a,
1275
+ normalizeBaseAIOptions as b,
1276
+ ContextLengthError as c,
1277
+ AuthenticationError as d,
925
1278
  extractRetryAfterSeconds as e,
926
- AIMessage as f,
927
- AIThread as g,
928
- AI_PROVIDER_TYPES as h,
929
- getAI as i,
930
- getAIAuto as j,
931
- getAIClient as k,
932
- getOpenAI as l
1279
+ extractTextContent as f,
1280
+ AIClient as g,
1281
+ AIMessage as h,
1282
+ AIThread as i,
1283
+ AI_PROVIDER_TYPES as j,
1284
+ DEFAULT_AI_MAX_RETRIES as k,
1285
+ DEFAULT_AI_TIMEOUT_MS as l,
1286
+ getAI as m,
1287
+ normalizeChatOptions as n,
1288
+ getAIAuto as o,
1289
+ prepareRequestControls as p,
1290
+ getAIClient as q,
1291
+ getOpenAI as r
933
1292
  };
934
- //# sourceMappingURL=index-BT4thAvS.js.map
1293
+ //# sourceMappingURL=index-DCXO0nZA.js.map