@umituz/web-ai 1.0.1 → 1.0.2

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.
@@ -184,6 +184,14 @@ var ProviderError = class extends AIError {
184
184
  providerId;
185
185
  };
186
186
 
187
+ // src/core/errors/groq-error.ts
188
+ var GroqError = class extends AIError {
189
+ constructor(type, message, cause, metadata = {}) {
190
+ super(type, message, cause, metadata);
191
+ this.name = "GroqError";
192
+ }
193
+ };
194
+
187
195
  // src/core/errors/error-classifier.ts
188
196
  var DEFAULT_RETRYABLE_TYPES = /* @__PURE__ */ new Set([
189
197
  "NETWORK_ERROR" /* NETWORK_ERROR */,
@@ -532,6 +540,56 @@ function isSseDoneMarker(payload) {
532
540
  return payload === SSE_DONE_MARKER;
533
541
  }
534
542
 
543
+ // src/core/parsers/streaming-response.parser.ts
544
+ var StreamingResponseParser = class {
545
+ constructor(callbacks, extractDelta) {
546
+ this.callbacks = callbacks;
547
+ this.extractDelta = extractDelta;
548
+ }
549
+ callbacks;
550
+ extractDelta;
551
+ decoder = new TextDecoder();
552
+ async parse(stream) {
553
+ const reader = stream.getReader();
554
+ const contentChunks = [];
555
+ let buffer = "";
556
+ const consumeLine = (rawLine) => {
557
+ const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
558
+ const trimmed = line.trim();
559
+ if (trimmed.length === 0 || !isSseDataLine(trimmed)) return;
560
+ const payload = extractSseDataPayload(trimmed);
561
+ if (isSseDoneMarker(payload)) return;
562
+ const content = this.extractDelta(payload);
563
+ if (content !== null) {
564
+ contentChunks.push(content);
565
+ this.callbacks.onChunk(content);
566
+ }
567
+ };
568
+ try {
569
+ while (true) {
570
+ const { done, value } = await reader.read();
571
+ if (done) break;
572
+ buffer += this.decoder.decode(value, { stream: true });
573
+ const lines = buffer.split("\n");
574
+ buffer = lines.pop() ?? "";
575
+ for (const line of lines) consumeLine(line);
576
+ }
577
+ buffer += this.decoder.decode();
578
+ if (buffer.length > 0) consumeLine(buffer);
579
+ } catch (error) {
580
+ if (error instanceof AIError) throw error;
581
+ throw new AIError(
582
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
583
+ "Stream was interrupted",
584
+ error instanceof Error ? error : void 0
585
+ );
586
+ } finally {
587
+ reader.releaseLock();
588
+ }
589
+ return contentChunks.join("");
590
+ }
591
+ };
592
+
535
593
  // src/core/parsers/json-content-extractor.ts
536
594
  function extractJsonFromCodeFence(content) {
537
595
  const match = content.match(JSON_CODE_BLOCK_PATTERN);
@@ -1190,6 +1248,12 @@ function calculateExponentialBackoff(params) {
1190
1248
  }
1191
1249
 
1192
1250
  // src/core/constants/identifier.constants.ts
1251
+ var MESSAGE_ID_PREFIX = "msg-";
1252
+ var CHAT_MESSAGE_ID_SUFFIX_LENGTH = 9;
1253
+ var CACHE_KEY_TYPE_COMPLETION = "completion";
1254
+ var CACHE_KEY_TYPE_CHAT = "chat";
1255
+ var USER_ROLE = "user";
1256
+ var ASSISTANT_ROLE = "assistant";
1193
1257
  var QUEUE_PRIORITY_USER_REQUEST = 10;
1194
1258
  var DEFAULT_RETRY_MAX_ATTEMPTS = 3;
1195
1259
  var DEFAULT_RETRY_INITIAL_DELAY_MS = 1e3;
@@ -1363,6 +1427,27 @@ var RequestDeduplicator = class {
1363
1427
  }
1364
1428
  };
1365
1429
 
1430
+ // src/core/utils/api-key-validator.ts
1431
+ var DEFAULT_API_KEY_RULE = {
1432
+ minLength: 10
1433
+ };
1434
+ function validateApiKey(apiKey, rule = DEFAULT_API_KEY_RULE) {
1435
+ if (!apiKey) {
1436
+ return { valid: false, reason: "API key is required" };
1437
+ }
1438
+ const trimmed = apiKey.trim();
1439
+ if (trimmed.length === 0) {
1440
+ return { valid: false, reason: "API key cannot be empty" };
1441
+ }
1442
+ if (trimmed.length < rule.minLength) {
1443
+ return {
1444
+ valid: false,
1445
+ reason: `API key must be at least ${rule.minLength} characters`
1446
+ };
1447
+ }
1448
+ return { valid: true };
1449
+ }
1450
+
1366
1451
  // src/providers/groq/groq.constants.ts
1367
1452
  var GROQ_MODELS = {
1368
1453
  LLAMA_3_1_8B_INSTANT: "llama-3.1-8b-instant",
@@ -1386,12 +1471,22 @@ var DEFAULT_GENERATION_CONFIG = {
1386
1471
  maxTokens: 1024,
1387
1472
  topP: 1
1388
1473
  };
1474
+ var DEFAULT_MODELS = {
1475
+ TEXT: GROQ_MODELS.LLAMA_3_1_8B_INSTANT,
1476
+ FAST: GROQ_MODELS.LLAMA_3_1_8B_INSTANT,
1477
+ EXPERIMENTAL: GROQ_MODELS.GPT_OSS_20B
1478
+ };
1389
1479
  var GROQ_BASE_URL = "https://api.groq.com/openai/v1";
1480
+ var DEFAULT_BASE_URL = GROQ_BASE_URL;
1390
1481
  var CHAT_COMPLETIONS_PATH = "/chat/completions";
1482
+ var API_ENDPOINTS = {
1483
+ CHAT: CHAT_COMPLETIONS_PATH
1484
+ };
1391
1485
  var GROQ_TIMEOUTS = {
1392
1486
  DEFAULT: 3e4,
1393
1487
  STREAMING: 6e4
1394
1488
  };
1489
+ var TIMEOUTS = GROQ_TIMEOUTS;
1395
1490
  var HTTP_HEADERS = {
1396
1491
  CONTENT_TYPE: "Content-Type",
1397
1492
  APPLICATION_JSON: "application/json",
@@ -1450,13 +1545,65 @@ var GroqChatRequestBuilder = class _GroqChatRequestBuilder {
1450
1545
  }
1451
1546
  };
1452
1547
 
1548
+ // src/providers/groq/groq-config-store.ts
1549
+ var GroqConfigStore = class {
1550
+ config = null;
1551
+ initialize(config) {
1552
+ const validation = validateApiKey(config.apiKey, { minLength: 1 });
1553
+ if (!validation.valid) {
1554
+ throw new GroqError("INVALID_API_KEY" /* INVALID_API_KEY */, validation.reason ?? "Invalid API key");
1555
+ }
1556
+ const normalized = {
1557
+ apiKey: config.apiKey.trim(),
1558
+ baseUrl: config.baseUrl ?? DEFAULT_BASE_URL,
1559
+ timeoutMs: config.timeoutMs ?? TIMEOUTS.DEFAULT,
1560
+ ...config.models !== void 0 ? { models: config.models } : {},
1561
+ ...config.textModel !== void 0 ? { textModel: config.textModel } : {}
1562
+ };
1563
+ this.config = normalized;
1564
+ return normalized;
1565
+ }
1566
+ get() {
1567
+ if (!this.config) {
1568
+ throw new GroqError(
1569
+ "MISSING_CONFIG" /* MISSING_CONFIG */,
1570
+ "Client not initialized. Call configureProvider first."
1571
+ );
1572
+ }
1573
+ return this.config;
1574
+ }
1575
+ getOrNull() {
1576
+ return this.config;
1577
+ }
1578
+ isInitialized() {
1579
+ return this.config !== null;
1580
+ }
1581
+ setApiKey(apiKey) {
1582
+ const current = this.get();
1583
+ const validation = validateApiKey(apiKey, { minLength: 1 });
1584
+ if (!validation.valid) {
1585
+ throw new GroqError("INVALID_API_KEY" /* INVALID_API_KEY */, validation.reason ?? "Invalid API key");
1586
+ }
1587
+ this.config = { ...current, apiKey: apiKey.trim() };
1588
+ }
1589
+ reset() {
1590
+ this.config = null;
1591
+ }
1592
+ };
1593
+
1453
1594
  // src/providers/groq/groq-http-transport.ts
1454
1595
  var GroqHttpTransport = class {
1455
- constructor(config) {
1456
- this.config = config;
1596
+ constructor(configSource) {
1597
+ this.configSource = configSource;
1457
1598
  }
1458
- config;
1599
+ configSource;
1459
1600
  pendingAbortControllers = /* @__PURE__ */ new Set();
1601
+ getConfig() {
1602
+ if ("get" in this.configSource && typeof this.configSource.get === "function") {
1603
+ return this.configSource.get();
1604
+ }
1605
+ return this.configSource;
1606
+ }
1460
1607
  cancelPendingRequests() {
1461
1608
  for (const controller of this.pendingAbortControllers) {
1462
1609
  controller.abort();
@@ -1471,7 +1618,7 @@ var GroqHttpTransport = class {
1471
1618
  }
1472
1619
  async streamChatCompletion(request, options = {}) {
1473
1620
  const url = `${this.baseUrl()}${CHAT_COMPLETIONS_PATH}`;
1474
- const timeout = this.config.timeoutMs ?? GROQ_TIMEOUTS.STREAMING;
1621
+ const timeout = this.getConfig().timeoutMs ?? GROQ_TIMEOUTS.STREAMING;
1475
1622
  const controller = new AbortController();
1476
1623
  const timeoutId = setTimeout(() => controller.abort(), timeout);
1477
1624
  const onExternalAbort = () => controller.abort();
@@ -1498,11 +1645,11 @@ var GroqHttpTransport = class {
1498
1645
  }
1499
1646
  }
1500
1647
  baseUrl() {
1501
- return this.config.baseUrl ?? GROQ_BASE_URL;
1648
+ return this.getConfig().baseUrl ?? GROQ_BASE_URL;
1502
1649
  }
1503
1650
  async request(endpoint, body) {
1504
1651
  const url = `${this.baseUrl()}${endpoint}`;
1505
- const timeout = this.config.timeoutMs ?? GROQ_TIMEOUTS.DEFAULT;
1652
+ const timeout = this.getConfig().timeoutMs ?? GROQ_TIMEOUTS.DEFAULT;
1506
1653
  const controller = new AbortController();
1507
1654
  const timeoutId = setTimeout(() => controller.abort(), timeout);
1508
1655
  this.pendingAbortControllers.add(controller);
@@ -1525,7 +1672,7 @@ var GroqHttpTransport = class {
1525
1672
  buildHeaders() {
1526
1673
  return {
1527
1674
  [HTTP_HEADERS.CONTENT_TYPE]: HTTP_HEADERS.APPLICATION_JSON,
1528
- [HTTP_HEADERS.AUTHORIZATION]: `${HTTP_HEADERS.BEARER_PREFIX}${this.config.apiKey}`
1675
+ [HTTP_HEADERS.AUTHORIZATION]: `${HTTP_HEADERS.BEARER_PREFIX}${this.getConfig().apiKey}`
1529
1676
  };
1530
1677
  }
1531
1678
  async throwFromErrorResponse(response) {
@@ -1627,21 +1774,40 @@ var CompositeExecutor = class {
1627
1774
 
1628
1775
  // src/providers/groq/groq-http-client.ts
1629
1776
  var GroqHttpClient = class {
1777
+ configStore;
1630
1778
  transport;
1631
1779
  executor;
1632
- constructor(config, deps = {}) {
1633
- this.transport = deps.transport ?? new GroqHttpTransport(config);
1780
+ constructor(configOrDeps, maybeDeps = {}) {
1781
+ const isConfig = typeof configOrDeps === "object" && configOrDeps !== null && "apiKey" in configOrDeps && typeof configOrDeps.apiKey === "string";
1782
+ const deps = isConfig ? maybeDeps : configOrDeps ?? {};
1783
+ this.configStore = deps.configStore ?? new GroqConfigStore();
1784
+ if (isConfig) {
1785
+ this.configStore.initialize(configOrDeps);
1786
+ }
1787
+ this.transport = deps.transport ?? new GroqHttpTransport(this.configStore);
1634
1788
  const retryManager2 = deps.retryManager ?? new RetryManager();
1635
1789
  const queue = deps.queue ?? new RequestQueue();
1636
1790
  const deduplicator = deps.deduplicator ?? new RequestDeduplicator();
1637
1791
  this.executor = new CompositeExecutor([
1638
1792
  new DedupeExecutor(deduplicator, {
1639
- generateKey: (request) => deduplicatorKey(request, config.apiKey)
1793
+ generateKey: (request) => deduplicatorKey(request, this.configStore.isInitialized() ? this.configStore.get().apiKey : "")
1640
1794
  }),
1641
1795
  new RetryExecutor(retryManager2),
1642
1796
  new QueueExecutor(queue, QUEUE_PRIORITY_USER_REQUEST)
1643
1797
  ]);
1644
1798
  }
1799
+ initialize(config) {
1800
+ this.configStore.initialize(config);
1801
+ }
1802
+ isInitialized() {
1803
+ return this.configStore.isInitialized();
1804
+ }
1805
+ getApiKey() {
1806
+ return this.configStore.get().apiKey;
1807
+ }
1808
+ setApiKey(apiKey) {
1809
+ this.configStore.setApiKey(apiKey);
1810
+ }
1645
1811
  cancelPendingRequests() {
1646
1812
  this.transport.cancelPendingRequests();
1647
1813
  }
@@ -1654,6 +1820,10 @@ var GroqHttpClient = class {
1654
1820
  streamChatCompletion(request, options) {
1655
1821
  return this.transport.streamChatCompletion(request, options);
1656
1822
  }
1823
+ reset() {
1824
+ this.transport.cancelPendingRequests();
1825
+ this.configStore.reset();
1826
+ }
1657
1827
  };
1658
1828
  function deduplicatorKey(request, apiKey) {
1659
1829
  const { model, messages, stream: _stream, ...rest } = request;
@@ -2530,38 +2700,554 @@ function createAI(config = {}) {
2530
2700
  return factory;
2531
2701
  }
2532
2702
 
2703
+ // src/providers/groq/groq-messages.ts
2704
+ var createUserMessage = (content) => ({
2705
+ role: "user",
2706
+ content
2707
+ });
2708
+ var createAssistantMessage = (content) => ({
2709
+ role: "assistant",
2710
+ content
2711
+ });
2712
+ var createSystemMessage = (content) => ({
2713
+ role: "system",
2714
+ content
2715
+ });
2716
+ var createMessage = (role, content) => ({
2717
+ role,
2718
+ content
2719
+ });
2720
+ var promptToMessages = (prompt) => [
2721
+ createUserMessage(prompt)
2722
+ ];
2723
+ var extractTextFromMessages = (messages) => messages.map((m) => m.content).join("\n\n");
2724
+ var formatMessagesForDisplay = (messages) => messages.map((m) => `[${m.role.toUpperCase()}]: ${m.content}`).join("\n\n---\n\n");
2725
+
2726
+ // src/providers/groq/groq-structured-json.ts
2727
+ async function requestStructuredJson(client, request) {
2728
+ const chatRequest = {
2729
+ model: request.model,
2730
+ messages: [createUserMessage(request.instructions)],
2731
+ temperature: request.temperature,
2732
+ max_tokens: request.maxTokens,
2733
+ response_format: { type: "json_object" },
2734
+ ...request.reasoningEffort !== void 0 ? { reasoning_effort: request.reasoningEffort } : {}
2735
+ };
2736
+ const response = await client.postChatCompletion(chatRequest);
2737
+ const content = response.choices[0]?.message?.content ?? "";
2738
+ const candidate = extractJsonCandidate(content);
2739
+ try {
2740
+ return JSON.parse(candidate);
2741
+ } catch (err) {
2742
+ throw new GroqError(
2743
+ "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
2744
+ "model response contained no parsable json object",
2745
+ err instanceof Error ? err : void 0
2746
+ );
2747
+ }
2748
+ }
2749
+
2750
+ // src/providers/groq/text-generation.service.ts
2751
+ var STRUCTURED_OUTPUT_TEMPERATURE = 0.1;
2752
+ var TextGenerationService = class {
2753
+ constructor(httpClient, cache = new CacheManager(), defaultModel = DEFAULT_MODELS.TEXT) {
2754
+ this.httpClient = httpClient;
2755
+ this.cache = cache;
2756
+ this.defaultModel = defaultModel;
2757
+ }
2758
+ httpClient;
2759
+ cache;
2760
+ defaultModel;
2761
+ id = "groq-text-generation";
2762
+ name = "Groq Text Generation";
2763
+ capabilities = ["text"];
2764
+ jsonParser = new JsonResponseParser();
2765
+ generateCompletion(prompt, options = {}) {
2766
+ const model = options.model ?? this.defaultModel;
2767
+ return this.executeWithCache(
2768
+ {
2769
+ type: CACHE_KEY_TYPE_COMPLETION,
2770
+ model,
2771
+ prompt,
2772
+ config: options.generationConfig
2773
+ },
2774
+ async () => {
2775
+ const request = GroqChatRequestBuilder.for(model).withMessages([createUserMessage(prompt)]).withConfig(options.generationConfig).build();
2776
+ const response = await this.httpClient.postChatCompletion(request);
2777
+ return this.extractContent(response.choices[0]?.message?.content);
2778
+ }
2779
+ );
2780
+ }
2781
+ generateChatCompletion(messages, options = {}) {
2782
+ const model = options.model ?? this.defaultModel;
2783
+ return this.executeWithCache(
2784
+ {
2785
+ type: CACHE_KEY_TYPE_CHAT,
2786
+ model,
2787
+ messages: [...messages],
2788
+ config: options.generationConfig
2789
+ },
2790
+ async () => {
2791
+ const request = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig(options.generationConfig).build();
2792
+ const response = await this.httpClient.postChatCompletion(request);
2793
+ return this.extractContent(response.choices[0]?.message?.content);
2794
+ }
2795
+ );
2796
+ }
2797
+ generateStructured(promptOrRequest, options) {
2798
+ if (typeof promptOrRequest === "string") {
2799
+ const model2 = options?.model ?? this.defaultModel;
2800
+ const jsonPrompt = StructuredPromptBuilder.for(promptOrRequest).withSchema(options?.schema).build();
2801
+ const request = GroqChatRequestBuilder.for(model2).withMessages([createUserMessage(jsonPrompt)]).withConfig({
2802
+ ...options?.generationConfig,
2803
+ temperature: STRUCTURED_OUTPUT_TEMPERATURE
2804
+ }).build();
2805
+ return this.httpClient.postChatCompletion(request).then((response) => {
2806
+ const content = this.extractContent(response.choices[0]?.message?.content);
2807
+ return this.jsonParser.parse(content);
2808
+ });
2809
+ }
2810
+ const req = promptOrRequest;
2811
+ const model = req.model ?? (req.tier ? this.resolveModel(req.tier) : this.defaultModel);
2812
+ const schemaObj = req.schema;
2813
+ const messages = [];
2814
+ if (req.systemPrompt) {
2815
+ messages.push({ role: "system", content: req.systemPrompt });
2816
+ }
2817
+ for (const m of req.messages) {
2818
+ messages.push({ role: m.role, content: m.content });
2819
+ }
2820
+ const lastMsg = messages[messages.length - 1];
2821
+ if (lastMsg) {
2822
+ const structuredPrompt = StructuredPromptBuilder.for(lastMsg.content).withSchema(schemaObj).build();
2823
+ messages[messages.length - 1] = { ...lastMsg, content: structuredPrompt };
2824
+ }
2825
+ const groqReq = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig({
2826
+ temperature: req.temperature ?? STRUCTURED_OUTPUT_TEMPERATURE,
2827
+ maxTokens: req.maxTokens
2828
+ }).build();
2829
+ return this.httpClient.postChatCompletion(groqReq).then((response) => {
2830
+ const content = this.extractContent(response.choices[0]?.message?.content);
2831
+ const parsed = this.jsonParser.parse(content);
2832
+ const genResult = {
2833
+ text: content,
2834
+ model: response.model,
2835
+ providerId: this.id,
2836
+ finishReason: response.choices[0]?.finish_reason ?? "stop",
2837
+ usage: response.usage ? {
2838
+ inputTokens: response.usage.prompt_tokens,
2839
+ outputTokens: response.usage.completion_tokens,
2840
+ totalTokens: response.usage.total_tokens
2841
+ } : void 0
2842
+ };
2843
+ return { value: parsed, result: genResult };
2844
+ });
2845
+ }
2846
+ streamCompletion(prompt, callbacks, options = {}) {
2847
+ return this.streamChatCompletion([createUserMessage(prompt)], callbacks, options);
2848
+ }
2849
+ async streamChatCompletion(messages, callbacks, options = {}) {
2850
+ const model = options.model ?? this.defaultModel;
2851
+ const request = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig(options.generationConfig).withStream(true).build();
2852
+ const parser = new StreamingResponseParser(
2853
+ {
2854
+ onChunk: (chunk) => callbacks.onChunk?.(chunk)
2855
+ },
2856
+ extractContentFromSsePayload
2857
+ );
2858
+ try {
2859
+ const stream = await this.httpClient.streamChatCompletion(request, {
2860
+ signal: options.signal
2861
+ });
2862
+ const fullText = await parser.parse(stream);
2863
+ callbacks.onComplete?.(fullText);
2864
+ return fullText;
2865
+ } catch (error) {
2866
+ const err = error instanceof Error ? error : new Error(String(error));
2867
+ callbacks.onError?.(err);
2868
+ throw err;
2869
+ }
2870
+ }
2871
+ clearCache() {
2872
+ this.cache.clear();
2873
+ }
2874
+ // --- ITextProvider implementation ---
2875
+ resolveModel(tier) {
2876
+ return GROQ_TIERS[tier] ?? this.defaultModel;
2877
+ }
2878
+ async generate(request) {
2879
+ const messages = [];
2880
+ if (request.systemPrompt) {
2881
+ messages.push({ role: "system", content: request.systemPrompt });
2882
+ }
2883
+ for (const m of request.messages) {
2884
+ messages.push({ role: m.role, content: m.content });
2885
+ }
2886
+ const model = request.model ?? (request.tier ? this.resolveModel(request.tier) : this.defaultModel);
2887
+ const groqReq = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig({
2888
+ temperature: request.temperature,
2889
+ maxTokens: request.maxTokens
2890
+ }).build();
2891
+ const resp = await this.httpClient.postChatCompletion(groqReq);
2892
+ const content = resp.choices[0]?.message?.content ?? "";
2893
+ return {
2894
+ text: content,
2895
+ finishReason: resp.choices[0]?.finish_reason ?? "stop",
2896
+ model: resp.model,
2897
+ providerId: this.id,
2898
+ usage: resp.usage ? {
2899
+ inputTokens: resp.usage.prompt_tokens,
2900
+ outputTokens: resp.usage.completion_tokens,
2901
+ totalTokens: resp.usage.total_tokens
2902
+ } : void 0
2903
+ };
2904
+ }
2905
+ async stream(request, callbacks) {
2906
+ const messages = [];
2907
+ if (request.systemPrompt) {
2908
+ messages.push({ role: "system", content: request.systemPrompt });
2909
+ }
2910
+ for (const m of request.messages) {
2911
+ messages.push({ role: m.role, content: m.content });
2912
+ }
2913
+ const model = request.model ?? (request.tier ? this.resolveModel(request.tier) : this.defaultModel);
2914
+ const text = await this.streamChatCompletion(
2915
+ messages,
2916
+ {
2917
+ onChunk: callbacks.onChunk,
2918
+ onError: callbacks.onError
2919
+ },
2920
+ {
2921
+ model,
2922
+ signal: request.signal,
2923
+ generationConfig: {
2924
+ temperature: request.temperature,
2925
+ maxTokens: request.maxTokens
2926
+ }
2927
+ }
2928
+ );
2929
+ const result = {
2930
+ text,
2931
+ finishReason: "stop",
2932
+ model,
2933
+ providerId: this.id
2934
+ };
2935
+ callbacks.onComplete?.(result);
2936
+ return result;
2937
+ }
2938
+ async countTokens(text) {
2939
+ return Math.max(1, Math.ceil(text.length / 4));
2940
+ }
2941
+ async estimateCost(_request) {
2942
+ return 0;
2943
+ }
2944
+ async healthCheck() {
2945
+ try {
2946
+ await this.generateCompletion("ping", {
2947
+ generationConfig: { maxTokens: 1 }
2948
+ });
2949
+ return "healthy";
2950
+ } catch {
2951
+ return "unavailable";
2952
+ }
2953
+ }
2954
+ async executeWithCache(params, factory) {
2955
+ const key = this.cache.generateKey(params);
2956
+ const cached = this.cache.get(key);
2957
+ if (cached !== null) {
2958
+ return cached;
2959
+ }
2960
+ const result = await factory();
2961
+ this.cache.set(key, result);
2962
+ return result;
2963
+ }
2964
+ extractContent(content) {
2965
+ if (!content) {
2966
+ throw new GroqError(
2967
+ "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
2968
+ "No content generated from Groq API"
2969
+ );
2970
+ }
2971
+ return content;
2972
+ }
2973
+ };
2974
+
2975
+ // src/core/calculations/random-base36-suffix.ts
2976
+ var BASE_36_RADIX = 36;
2977
+ var DECIMAL_OFFSET = 2;
2978
+ function generateRandomBase36Suffix(length, random = Math.random) {
2979
+ if (length <= 0) return "";
2980
+ const raw = random().toString(BASE_36_RADIX).slice(DECIMAL_OFFSET);
2981
+ if (raw.length >= length) return raw.slice(0, length);
2982
+ let result = raw;
2983
+ while (result.length < length) {
2984
+ result += random().toString(BASE_36_RADIX).slice(DECIMAL_OFFSET);
2985
+ }
2986
+ return result.slice(0, length);
2987
+ }
2988
+
2989
+ // src/chat/utils/chat-id-generator.ts
2990
+ var ID_PART_DELIMITER = "-";
2991
+ var ChatIdGenerator = class {
2992
+ counter = 0;
2993
+ randomLength;
2994
+ constructor(randomLength = CHAT_MESSAGE_ID_SUFFIX_LENGTH) {
2995
+ this.randomLength = randomLength;
2996
+ }
2997
+ next(sender) {
2998
+ this.counter += 1;
2999
+ const random = generateRandomBase36Suffix(this.randomLength);
3000
+ return [
3001
+ MESSAGE_ID_PREFIX,
3002
+ Date.now(),
3003
+ sender,
3004
+ this.counter,
3005
+ random
3006
+ ].join(ID_PART_DELIMITER);
3007
+ }
3008
+ };
3009
+ var chatIdGenerator = new ChatIdGenerator();
3010
+
3011
+ // src/chat/utils/message-formatter.ts
3012
+ var MessageFormatter = class {
3013
+ constructor(idGenerator = chatIdGenerator) {
3014
+ this.idGenerator = idGenerator;
3015
+ }
3016
+ idGenerator;
3017
+ /** Maps chat-domain history to neutral core messages; system senders are
3018
+ * dropped — the system prompt travels on the request's own slot. */
3019
+ toCoreMessages(messages) {
3020
+ const coreMessages = [];
3021
+ for (const message of messages) {
3022
+ if (message.sender === "system") continue;
3023
+ coreMessages.push({
3024
+ role: message.sender === "user" ? USER_ROLE : ASSISTANT_ROLE,
3025
+ content: message.content
3026
+ });
3027
+ }
3028
+ return coreMessages;
3029
+ }
3030
+ toChatMessage(content, sender) {
3031
+ return {
3032
+ id: this.idGenerator.next(sender),
3033
+ sender,
3034
+ content: content.trim(),
3035
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3036
+ type: "text"
3037
+ };
3038
+ }
3039
+ };
3040
+ var messageFormatter = new MessageFormatter();
3041
+
3042
+ // src/chat/utils/prompt-strategy.ts
3043
+ var DEFAULT_PROMPT_STRATEGY = {
3044
+ resolve: (config) => {
3045
+ if (config.systemPrompt && config.systemPrompt.length > 0) {
3046
+ return config.systemPrompt;
3047
+ }
3048
+ return void 0;
3049
+ }
3050
+ };
3051
+
3052
+ // src/chat/services/chat.service.ts
3053
+ var DEFAULT_CONFIG = {
3054
+ temperature: 0.8,
3055
+ maxTokens: 500
3056
+ };
3057
+ var ChatService = class {
3058
+ constructor(engine) {
3059
+ this.engine = engine;
3060
+ }
3061
+ engine;
3062
+ config = DEFAULT_CONFIG;
3063
+ promptStrategy = DEFAULT_PROMPT_STRATEGY;
3064
+ storage = null;
3065
+ initialize(config) {
3066
+ this.config = { ...DEFAULT_CONFIG, ...config };
3067
+ }
3068
+ updateConfig(config) {
3069
+ this.config = { ...this.config, ...config };
3070
+ }
3071
+ setPromptStrategy(strategy) {
3072
+ this.promptStrategy = strategy;
3073
+ }
3074
+ setStorage(storage) {
3075
+ this.storage = storage;
3076
+ }
3077
+ async getConversation(conversationId) {
3078
+ const storage = this.requireStorage();
3079
+ const messages = await storage.getMessages(conversationId);
3080
+ if (messages.length === 0) return null;
3081
+ const last = messages[messages.length - 1];
3082
+ const first = messages[0];
3083
+ const companionId = String(first.metadata?.["companionId"] ?? "unknown");
3084
+ const companionName = String(first.metadata?.["companionName"] ?? "Unknown");
3085
+ return {
3086
+ id: conversationId,
3087
+ companionId,
3088
+ companionName,
3089
+ lastMessage: last,
3090
+ messageCount: messages.length,
3091
+ updatedAt: last.timestamp
3092
+ };
3093
+ }
3094
+ async getConversations() {
3095
+ if (this.storage && "getConversations" in this.storage && typeof this.storage.getConversations === "function") {
3096
+ return this.storage.getConversations();
3097
+ }
3098
+ return [];
3099
+ }
3100
+ async sendMessage(input, signal) {
3101
+ const userMessage = {
3102
+ ...messageFormatter.toChatMessage(input.text, "user"),
3103
+ type: input.type ?? "text",
3104
+ ...input.imageUrl !== void 0 ? { imageUrl: input.imageUrl } : {}
3105
+ };
3106
+ const aiResponse = await this.generateAIResponse(
3107
+ input.companionId,
3108
+ input.text,
3109
+ [],
3110
+ signal
3111
+ );
3112
+ return { userMessage, aiResponse };
3113
+ }
3114
+ async generateAIResponse(companionId, userMessage, context = [], signal) {
3115
+ const result = await this.run({
3116
+ ...this.buildRequest(userMessage, context, signal)
3117
+ });
3118
+ return {
3119
+ ...messageFormatter.toChatMessage(result.text, "assistant"),
3120
+ metadata: {
3121
+ companionId,
3122
+ model: result.model,
3123
+ usage: {
3124
+ input_tokens: result.usage?.inputTokens,
3125
+ output_tokens: result.usage?.outputTokens,
3126
+ total_tokens: result.usage?.totalTokens
3127
+ },
3128
+ finishReason: result.finishReason
3129
+ }
3130
+ };
3131
+ }
3132
+ async generateStreamingResponse(userMessage, context, onChunk, signal) {
3133
+ const request = this.buildRequest(userMessage, context, signal);
3134
+ const result = await this.runStream(request, { onChunk });
3135
+ return result.text;
3136
+ }
3137
+ buildRequest(userMessage, context, signal) {
3138
+ const messages = messageFormatter.toCoreMessages(context);
3139
+ messages.push({ role: "user", content: userMessage });
3140
+ return {
3141
+ messages,
3142
+ systemPrompt: this.resolveSystemPrompt(),
3143
+ model: this.config.model,
3144
+ tier: this.config.tier,
3145
+ temperature: this.config.temperature,
3146
+ maxTokens: this.config.maxTokens,
3147
+ ...signal !== void 0 ? { signal } : {}
3148
+ };
3149
+ }
3150
+ resolveSystemPrompt() {
3151
+ const prompt = this.promptStrategy.resolve(this.config);
3152
+ return prompt && prompt.length > 0 ? prompt : void 0;
3153
+ }
3154
+ async run(request) {
3155
+ if (this.engine instanceof ProviderFactory) {
3156
+ return this.engine.chat(request);
3157
+ }
3158
+ return this.engine.generate(request);
3159
+ }
3160
+ async runStream(request, callbacks) {
3161
+ if (this.engine instanceof ProviderFactory) {
3162
+ return this.engine.chatStream(request, callbacks);
3163
+ }
3164
+ return this.engine.stream(request, callbacks);
3165
+ }
3166
+ requireStorage() {
3167
+ if (!this.storage) {
3168
+ throw new AIError(
3169
+ "MISSING_CONFIG" /* MISSING_CONFIG */,
3170
+ "ChatService.getConversation requires setStorage() to be called first."
3171
+ );
3172
+ }
3173
+ return this.storage;
3174
+ }
3175
+ };
3176
+
3177
+ // src/providers/groq/groq-container.ts
3178
+ var container = null;
3179
+ function configureProvider(config) {
3180
+ const configStore = new GroqConfigStore();
3181
+ const transport = new GroqHttpTransport(configStore);
3182
+ const httpClient = new GroqHttpClient({ configStore, transport });
3183
+ httpClient.initialize(config);
3184
+ const textGeneration = new TextGenerationService(
3185
+ httpClient,
3186
+ new CacheManager(),
3187
+ config.textModel
3188
+ );
3189
+ const chat = new ChatService(textGeneration);
3190
+ container = { groqHttpClient: httpClient, textGeneration, chat };
3191
+ return container;
3192
+ }
3193
+ function getProvider() {
3194
+ return container;
3195
+ }
3196
+ function resetProvider() {
3197
+ container?.groqHttpClient.reset();
3198
+ container = null;
3199
+ }
3200
+
2533
3201
  exports.ANTHROPIC_API_VERSION = ANTHROPIC_API_VERSION;
2534
3202
  exports.ANTHROPIC_DEFAULT_MAX_TOKENS = ANTHROPIC_DEFAULT_MAX_TOKENS;
2535
3203
  exports.ANTHROPIC_MODELS = ANTHROPIC_MODELS;
2536
3204
  exports.ANTHROPIC_TIERS = ANTHROPIC_TIERS;
3205
+ exports.API_ENDPOINTS = API_ENDPOINTS;
2537
3206
  exports.AnthropicHttpTransport = AnthropicHttpTransport;
2538
3207
  exports.AnthropicProvider = AnthropicProvider;
2539
3208
  exports.AnthropicRequestBuilder = AnthropicRequestBuilder;
2540
3209
  exports.BaseProvider = BaseProvider;
3210
+ exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL;
3211
+ exports.DEFAULT_GENERATION_CONFIG = DEFAULT_GENERATION_CONFIG;
3212
+ exports.DEFAULT_MODELS = DEFAULT_MODELS;
2541
3213
  exports.FalProvider = FalProvider;
2542
3214
  exports.GEMINI_TIERS = GEMINI_TIERS;
2543
3215
  exports.GROQ_MODELS = GROQ_MODELS;
2544
3216
  exports.GROQ_TIERS = GROQ_TIERS;
2545
3217
  exports.GeminiProvider = GeminiProvider;
2546
3218
  exports.GroqChatRequestBuilder = GroqChatRequestBuilder;
3219
+ exports.GroqConfigStore = GroqConfigStore;
2547
3220
  exports.GroqHttpClient = GroqHttpClient;
2548
3221
  exports.GroqHttpTransport = GroqHttpTransport;
2549
3222
  exports.GroqProvider = GroqProvider;
2550
3223
  exports.ProviderError = ProviderError;
2551
3224
  exports.ProviderFactory = ProviderFactory;
2552
3225
  exports.PrunaProvider = PrunaProvider;
3226
+ exports.TIMEOUTS = TIMEOUTS;
3227
+ exports.TextGenerationService = TextGenerationService;
3228
+ exports.configureProvider = configureProvider;
2553
3229
  exports.consumeAnthropicStream = consumeAnthropicStream;
2554
3230
  exports.consumeGroqStream = consumeGroqStream;
2555
3231
  exports.createAI = createAI;
3232
+ exports.createAssistantMessage = createAssistantMessage;
2556
3233
  exports.createFalProvider = createFalProvider;
2557
3234
  exports.createGeminiProvider = createGeminiProvider;
3235
+ exports.createMessage = createMessage;
2558
3236
  exports.createPrunaProvider = createPrunaProvider;
3237
+ exports.createSystemMessage = createSystemMessage;
3238
+ exports.createUserMessage = createUserMessage;
2559
3239
  exports.defaultEffortForModel = defaultEffortForModel;
2560
3240
  exports.extractContentFromSsePayload = extractContentFromSsePayload;
3241
+ exports.extractTextFromMessages = extractTextFromMessages;
3242
+ exports.formatMessagesForDisplay = formatMessagesForDisplay;
2561
3243
  exports.generateWithPruna = generateWithPruna;
3244
+ exports.getProvider = getProvider;
2562
3245
  exports.isMediaProvider = isMediaProvider;
2563
3246
  exports.isTextProvider = isTextProvider;
2564
3247
  exports.mapAnthropicStopReason = mapAnthropicStopReason;
3248
+ exports.promptToMessages = promptToMessages;
3249
+ exports.requestStructuredJson = requestStructuredJson;
3250
+ exports.resetProvider = resetProvider;
2565
3251
  exports.toGroqMessages = toGroqMessages;
2566
3252
  //# sourceMappingURL=index.cjs.map
2567
3253
  //# sourceMappingURL=index.cjs.map