@yourgpt/llm-sdk 2.0.1 → 2.0.2-beta.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.
Files changed (55) hide show
  1. package/dist/adapters/index.d.mts +27 -4
  2. package/dist/adapters/index.d.ts +27 -4
  3. package/dist/adapters/index.js +395 -25
  4. package/dist/adapters/index.js.map +1 -1
  5. package/dist/adapters/index.mjs +395 -25
  6. package/dist/adapters/index.mjs.map +1 -1
  7. package/dist/index.d.mts +24 -5
  8. package/dist/index.d.ts +24 -5
  9. package/dist/index.js +20 -4
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +20 -4
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/providers/anthropic/index.d.mts +1 -2
  14. package/dist/providers/anthropic/index.d.ts +1 -2
  15. package/dist/providers/anthropic/index.js +108 -12
  16. package/dist/providers/anthropic/index.js.map +1 -1
  17. package/dist/providers/anthropic/index.mjs +108 -12
  18. package/dist/providers/anthropic/index.mjs.map +1 -1
  19. package/dist/providers/azure/index.d.mts +1 -2
  20. package/dist/providers/azure/index.d.ts +1 -2
  21. package/dist/providers/azure/index.js.map +1 -1
  22. package/dist/providers/azure/index.mjs.map +1 -1
  23. package/dist/providers/google/index.d.mts +1 -2
  24. package/dist/providers/google/index.d.ts +1 -2
  25. package/dist/providers/google/index.js +61 -2
  26. package/dist/providers/google/index.js.map +1 -1
  27. package/dist/providers/google/index.mjs +61 -2
  28. package/dist/providers/google/index.mjs.map +1 -1
  29. package/dist/providers/ollama/index.d.mts +8 -3
  30. package/dist/providers/ollama/index.d.ts +8 -3
  31. package/dist/providers/ollama/index.js +227 -17
  32. package/dist/providers/ollama/index.js.map +1 -1
  33. package/dist/providers/ollama/index.mjs +227 -17
  34. package/dist/providers/ollama/index.mjs.map +1 -1
  35. package/dist/providers/openai/index.d.mts +1 -2
  36. package/dist/providers/openai/index.d.ts +1 -2
  37. package/dist/providers/openai/index.js +57 -3
  38. package/dist/providers/openai/index.js.map +1 -1
  39. package/dist/providers/openai/index.mjs +57 -3
  40. package/dist/providers/openai/index.mjs.map +1 -1
  41. package/dist/providers/openrouter/index.d.mts +56 -3
  42. package/dist/providers/openrouter/index.d.ts +56 -3
  43. package/dist/providers/openrouter/index.js +90 -276
  44. package/dist/providers/openrouter/index.js.map +1 -1
  45. package/dist/providers/openrouter/index.mjs +89 -277
  46. package/dist/providers/openrouter/index.mjs.map +1 -1
  47. package/dist/providers/xai/index.d.mts +1 -2
  48. package/dist/providers/xai/index.d.ts +1 -2
  49. package/dist/providers/xai/index.js.map +1 -1
  50. package/dist/providers/xai/index.mjs.map +1 -1
  51. package/dist/{base-DdxolpKP.d.mts → types-C_f95PKp.d.mts} +434 -3
  52. package/dist/{base-DdxolpKP.d.ts → types-C_f95PKp.d.ts} +434 -3
  53. package/package.json +1 -1
  54. package/dist/types-Ck25ZYma.d.mts +0 -323
  55. package/dist/types-Dsz8SpdB.d.ts +0 -323
@@ -361,29 +361,60 @@ var OpenAIAdapter = class {
361
361
  request.systemPrompt
362
362
  );
363
363
  }
364
- const tools = request.actions?.length ? formatTools(request.actions) : void 0;
364
+ const tools = request.actions?.length ? formatTools(request.actions) : [];
365
+ const webSearchConfig = request.webSearch ?? this.config.webSearch;
366
+ if (webSearchConfig) {
367
+ const webSearchTool = {
368
+ type: "web_search_preview"
369
+ };
370
+ const wsConfig = typeof webSearchConfig === "object" ? webSearchConfig : {};
371
+ if (wsConfig.userLocation) {
372
+ webSearchTool.search_context_size = "medium";
373
+ }
374
+ tools.push(webSearchTool);
375
+ }
365
376
  const messageId = generateMessageId();
366
377
  yield { type: "message:start", id: messageId };
367
378
  try {
368
379
  const stream = await client.chat.completions.create({
369
380
  model: request.config?.model || this.model,
370
381
  messages,
371
- tools,
382
+ tools: tools.length > 0 ? tools : void 0,
372
383
  temperature: request.config?.temperature ?? this.config.temperature,
373
384
  max_tokens: request.config?.maxTokens ?? this.config.maxTokens,
374
385
  stream: true,
375
386
  stream_options: { include_usage: true }
376
387
  });
377
388
  let currentToolCall = null;
389
+ const collectedCitations = [];
390
+ let citationIndex = 0;
378
391
  let usage;
379
392
  for await (const chunk of stream) {
380
393
  if (request.signal?.aborted) {
381
394
  break;
382
395
  }
383
396
  const delta = chunk.choices[0]?.delta;
397
+ const choice = chunk.choices[0];
384
398
  if (delta?.content) {
385
399
  yield { type: "message:delta", content: delta.content };
386
400
  }
401
+ const annotations = delta?.annotations;
402
+ if (annotations && annotations.length > 0) {
403
+ for (const annotation of annotations) {
404
+ if (annotation.type === "url_citation" && annotation.url_citation?.url) {
405
+ citationIndex++;
406
+ const url = annotation.url_citation.url;
407
+ const domain = extractDomain(url);
408
+ collectedCitations.push({
409
+ index: citationIndex,
410
+ url,
411
+ title: annotation.url_citation.title || domain,
412
+ domain,
413
+ favicon: domain ? `https://www.google.com/s2/favicons?domain=${domain}&sz=32` : void 0
414
+ });
415
+ }
416
+ }
417
+ }
387
418
  if (delta?.tool_calls) {
388
419
  for (const toolCall of delta.tool_calls) {
389
420
  if (toolCall.id) {
@@ -416,7 +447,7 @@ var OpenAIAdapter = class {
416
447
  total_tokens: chunk.usage.total_tokens
417
448
  };
418
449
  }
419
- if (chunk.choices[0]?.finish_reason) {
450
+ if (choice?.finish_reason) {
420
451
  if (currentToolCall) {
421
452
  yield {
422
453
  type: "action:args",
@@ -426,6 +457,10 @@ var OpenAIAdapter = class {
426
457
  }
427
458
  }
428
459
  }
460
+ if (collectedCitations.length > 0) {
461
+ const uniqueCitations = deduplicateCitations(collectedCitations);
462
+ yield { type: "citation", citations: uniqueCitations };
463
+ }
429
464
  yield { type: "message:end" };
430
465
  yield { type: "done", usage };
431
466
  } catch (error) {
@@ -437,6 +472,25 @@ var OpenAIAdapter = class {
437
472
  }
438
473
  }
439
474
  };
475
+ function extractDomain(url) {
476
+ try {
477
+ const parsed = new URL(url);
478
+ return parsed.hostname;
479
+ } catch {
480
+ return "";
481
+ }
482
+ }
483
+ function deduplicateCitations(citations) {
484
+ const seen = /* @__PURE__ */ new Map();
485
+ let index = 0;
486
+ for (const citation of citations) {
487
+ if (!seen.has(citation.url)) {
488
+ index++;
489
+ seen.set(citation.url, { ...citation, index });
490
+ }
491
+ }
492
+ return Array.from(seen.values());
493
+ }
440
494
  function createOpenAIAdapter(config) {
441
495
  return new OpenAIAdapter(config);
442
496
  }
@@ -635,14 +689,45 @@ var AnthropicAdapter = class {
635
689
  ) : {},
636
690
  required: action.parameters ? Object.entries(action.parameters).filter(([, param]) => param.required).map(([key]) => key) : []
637
691
  }
638
- }));
692
+ })) || [];
693
+ const webSearchConfig = request.webSearch ?? this.config.webSearch;
694
+ let serverToolConfiguration;
695
+ if (webSearchConfig) {
696
+ tools.push({
697
+ type: "web_search_20260209",
698
+ name: "web_search",
699
+ allowed_callers: ["direct"]
700
+ });
701
+ const wsConfig = typeof webSearchConfig === "object" ? webSearchConfig : {};
702
+ const webSearchToolConfig = {};
703
+ if (wsConfig.maxUses !== void 0) {
704
+ webSearchToolConfig.max_uses = wsConfig.maxUses;
705
+ }
706
+ if (wsConfig.allowedDomains && wsConfig.allowedDomains.length > 0) {
707
+ webSearchToolConfig.allowed_domains = wsConfig.allowedDomains;
708
+ }
709
+ if (wsConfig.blockedDomains && wsConfig.blockedDomains.length > 0) {
710
+ webSearchToolConfig.blocked_domains = wsConfig.blockedDomains;
711
+ }
712
+ if (wsConfig.userLocation) {
713
+ webSearchToolConfig.user_location = wsConfig.userLocation;
714
+ }
715
+ if (Object.keys(webSearchToolConfig).length > 0) {
716
+ serverToolConfiguration = {
717
+ web_search: webSearchToolConfig
718
+ };
719
+ }
720
+ }
639
721
  const options = {
640
722
  model: request.config?.model || this.model,
641
723
  max_tokens: request.config?.maxTokens || this.config.maxTokens || 4096,
642
724
  system: systemMessage,
643
725
  messages,
644
- tools: tools?.length ? tools : void 0
726
+ tools: tools.length ? tools : void 0
645
727
  };
728
+ if (serverToolConfiguration) {
729
+ options.server_tool_configuration = serverToolConfiguration;
730
+ }
646
731
  if (this.config.thinking?.type === "enabled") {
647
732
  options.thinking = {
648
733
  type: "enabled",
@@ -698,6 +783,8 @@ var AnthropicAdapter = class {
698
783
  const stream = await client.messages.stream(options);
699
784
  let currentToolUse = null;
700
785
  let isInThinkingBlock = false;
786
+ const collectedCitations = [];
787
+ let citationIndex = 0;
701
788
  let usage;
702
789
  for await (const event of stream) {
703
790
  if (request.signal?.aborted) {
@@ -726,11 +813,30 @@ var AnthropicAdapter = class {
726
813
  name: event.content_block.name,
727
814
  input: ""
728
815
  };
729
- yield {
730
- type: "action:start",
731
- id: currentToolUse.id,
732
- name: currentToolUse.name
733
- };
816
+ if (currentToolUse.name !== "web_search") {
817
+ yield {
818
+ type: "action:start",
819
+ id: currentToolUse.id,
820
+ name: currentToolUse.name
821
+ };
822
+ }
823
+ } else if (event.content_block.type === "web_search_tool_result") {
824
+ const webSearchResult = event.content_block;
825
+ if (webSearchResult.content && Array.isArray(webSearchResult.content)) {
826
+ for (const result of webSearchResult.content) {
827
+ if (result.type === "web_search_result" && result.url) {
828
+ citationIndex++;
829
+ const domain = extractDomain2(result.url);
830
+ collectedCitations.push({
831
+ index: citationIndex,
832
+ url: result.url,
833
+ title: result.title || domain,
834
+ domain,
835
+ favicon: domain ? `https://www.google.com/s2/favicons?domain=${domain}&sz=32` : void 0
836
+ });
837
+ }
838
+ }
839
+ }
734
840
  } else if (event.content_block.type === "thinking") {
735
841
  isInThinkingBlock = true;
736
842
  yield { type: "thinking:start" };
@@ -739,6 +845,20 @@ var AnthropicAdapter = class {
739
845
  case "content_block_delta":
740
846
  if (event.delta.type === "text_delta") {
741
847
  yield { type: "message:delta", content: event.delta.text };
848
+ } else if (event.delta.type === "citations_delta") {
849
+ const citationsDelta = event.delta;
850
+ if (citationsDelta.citation?.url) {
851
+ citationIndex++;
852
+ const domain = extractDomain2(citationsDelta.citation.url);
853
+ collectedCitations.push({
854
+ index: citationIndex,
855
+ url: citationsDelta.citation.url,
856
+ title: citationsDelta.citation.title || domain,
857
+ citedText: citationsDelta.citation.cited_text,
858
+ domain,
859
+ favicon: domain ? `https://www.google.com/s2/favicons?domain=${domain}&sz=32` : void 0
860
+ });
861
+ }
742
862
  } else if (event.delta.type === "thinking_delta") {
743
863
  yield { type: "thinking:delta", content: event.delta.thinking };
744
864
  } else if (event.delta.type === "input_json_delta" && currentToolUse) {
@@ -747,11 +867,18 @@ var AnthropicAdapter = class {
747
867
  break;
748
868
  case "content_block_stop":
749
869
  if (currentToolUse) {
750
- yield {
751
- type: "action:args",
752
- id: currentToolUse.id,
753
- args: currentToolUse.input
754
- };
870
+ if (currentToolUse.name !== "web_search") {
871
+ yield {
872
+ type: "action:args",
873
+ id: currentToolUse.id,
874
+ args: currentToolUse.input
875
+ };
876
+ yield {
877
+ type: "action:end",
878
+ id: currentToolUse.id,
879
+ name: currentToolUse.name
880
+ };
881
+ }
755
882
  currentToolUse = null;
756
883
  }
757
884
  if (isInThinkingBlock) {
@@ -763,6 +890,10 @@ var AnthropicAdapter = class {
763
890
  break;
764
891
  }
765
892
  }
893
+ if (collectedCitations.length > 0) {
894
+ const uniqueCitations = deduplicateCitations2(collectedCitations);
895
+ yield { type: "citation", citations: uniqueCitations };
896
+ }
766
897
  yield { type: "message:end" };
767
898
  yield { type: "done", usage };
768
899
  } catch (error) {
@@ -774,11 +905,136 @@ var AnthropicAdapter = class {
774
905
  }
775
906
  }
776
907
  };
908
+ function extractDomain2(url) {
909
+ try {
910
+ const parsed = new URL(url);
911
+ return parsed.hostname;
912
+ } catch {
913
+ return "";
914
+ }
915
+ }
916
+ function deduplicateCitations2(citations) {
917
+ const seen = /* @__PURE__ */ new Map();
918
+ let index = 0;
919
+ for (const citation of citations) {
920
+ if (!seen.has(citation.url)) {
921
+ index++;
922
+ seen.set(citation.url, { ...citation, index });
923
+ }
924
+ }
925
+ return Array.from(seen.values());
926
+ }
777
927
  function createAnthropicAdapter(config) {
778
928
  return new AnthropicAdapter(config);
779
929
  }
780
930
 
781
931
  // src/adapters/ollama.ts
932
+ function extractBase64Data(data) {
933
+ if (data.startsWith("data:")) {
934
+ const commaIndex = data.indexOf(",");
935
+ if (commaIndex !== -1) {
936
+ return data.slice(commaIndex + 1);
937
+ }
938
+ }
939
+ return data;
940
+ }
941
+ async function downloadImageAsBase64(url) {
942
+ try {
943
+ const response = await fetch(url);
944
+ if (!response.ok) return null;
945
+ const buffer = await response.arrayBuffer();
946
+ const base64 = Buffer.from(buffer).toString("base64");
947
+ return base64;
948
+ } catch {
949
+ return null;
950
+ }
951
+ }
952
+ async function extractImagesFromAttachments(attachments) {
953
+ if (!attachments) return [];
954
+ const images = [];
955
+ for (const attachment of attachments) {
956
+ if (attachment.type !== "image") continue;
957
+ if (attachment.data) {
958
+ images.push(extractBase64Data(attachment.data));
959
+ } else if (attachment.url) {
960
+ const base64 = await downloadImageAsBase64(attachment.url);
961
+ if (base64) {
962
+ images.push(base64);
963
+ }
964
+ }
965
+ }
966
+ return images;
967
+ }
968
+ async function formatMessagesForOllama(messages, systemPrompt) {
969
+ const formatted = [];
970
+ if (systemPrompt) {
971
+ formatted.push({ role: "system", content: systemPrompt });
972
+ }
973
+ for (const msg of messages) {
974
+ const baseMessage = {
975
+ role: msg.role,
976
+ content: msg.content ?? ""
977
+ };
978
+ if (msg.role === "user" && msg.metadata?.attachments) {
979
+ const images = await extractImagesFromAttachments(
980
+ msg.metadata.attachments
981
+ );
982
+ if (images.length > 0) {
983
+ baseMessage.images = images;
984
+ }
985
+ }
986
+ if (msg.role === "assistant" && msg.tool_calls && msg.tool_calls.length > 0) {
987
+ baseMessage.tool_calls = msg.tool_calls.map((tc) => ({
988
+ id: tc.id,
989
+ function: {
990
+ name: tc.function.name,
991
+ arguments: JSON.parse(tc.function.arguments)
992
+ }
993
+ }));
994
+ }
995
+ if (msg.role === "tool" && msg.tool_call_id) {
996
+ baseMessage.tool_call_id = msg.tool_call_id;
997
+ }
998
+ formatted.push(baseMessage);
999
+ }
1000
+ return formatted;
1001
+ }
1002
+ async function processRawMessagesForOllama(rawMessages, systemPrompt) {
1003
+ const processed = [];
1004
+ if (systemPrompt) {
1005
+ const hasSystem = rawMessages.some((m) => m.role === "system");
1006
+ if (!hasSystem) {
1007
+ processed.push({ role: "system", content: systemPrompt });
1008
+ }
1009
+ }
1010
+ for (const msg of rawMessages) {
1011
+ const baseMessage = {
1012
+ role: msg.role,
1013
+ content: msg.content ?? ""
1014
+ };
1015
+ if (msg.role === "user" && msg.attachments && Array.isArray(msg.attachments)) {
1016
+ const attachments = msg.attachments;
1017
+ const images = await extractImagesFromAttachments(attachments);
1018
+ if (images.length > 0) {
1019
+ baseMessage.images = images;
1020
+ }
1021
+ }
1022
+ if (msg.tool_calls && Array.isArray(msg.tool_calls)) {
1023
+ baseMessage.tool_calls = msg.tool_calls.map((tc) => ({
1024
+ id: tc.id,
1025
+ function: {
1026
+ name: tc.function.name,
1027
+ arguments: typeof tc.function.arguments === "string" ? JSON.parse(tc.function.arguments) : tc.function.arguments
1028
+ }
1029
+ }));
1030
+ }
1031
+ if (msg.tool_call_id) {
1032
+ baseMessage.tool_call_id = msg.tool_call_id;
1033
+ }
1034
+ processed.push(baseMessage);
1035
+ }
1036
+ return processed;
1037
+ }
782
1038
  var OllamaAdapter = class {
783
1039
  constructor(config = {}) {
784
1040
  this.provider = "ollama";
@@ -787,10 +1043,32 @@ var OllamaAdapter = class {
787
1043
  this.baseUrl = config.baseUrl || "http://localhost:11434";
788
1044
  }
789
1045
  async *stream(request) {
790
- const messages = formatMessages(request.messages, request.systemPrompt);
1046
+ let messages;
1047
+ if (request.rawMessages && request.rawMessages.length > 0) {
1048
+ messages = await processRawMessagesForOllama(
1049
+ request.rawMessages,
1050
+ request.systemPrompt
1051
+ );
1052
+ } else {
1053
+ messages = await formatMessagesForOllama(
1054
+ request.messages,
1055
+ request.systemPrompt
1056
+ );
1057
+ }
1058
+ const tools = request.actions?.length ? formatTools(request.actions) : void 0;
791
1059
  const messageId = generateMessageId();
792
1060
  yield { type: "message:start", id: messageId };
793
1061
  try {
1062
+ const ollamaOptions = {
1063
+ temperature: request.config?.temperature ?? this.config.temperature
1064
+ };
1065
+ const maxTokens = request.config?.maxTokens ?? this.config.maxTokens;
1066
+ if (maxTokens !== void 0) {
1067
+ ollamaOptions.num_predict = maxTokens;
1068
+ }
1069
+ if (this.config.options) {
1070
+ Object.assign(ollamaOptions, this.config.options);
1071
+ }
794
1072
  const response = await fetch(`${this.baseUrl}/api/chat`, {
795
1073
  method: "POST",
796
1074
  headers: {
@@ -799,16 +1077,15 @@ var OllamaAdapter = class {
799
1077
  body: JSON.stringify({
800
1078
  model: request.config?.model || this.model,
801
1079
  messages,
1080
+ tools,
802
1081
  stream: true,
803
- options: {
804
- temperature: request.config?.temperature ?? this.config.temperature,
805
- num_predict: request.config?.maxTokens ?? this.config.maxTokens
806
- }
1082
+ options: ollamaOptions
807
1083
  }),
808
1084
  signal: request.signal
809
1085
  });
810
1086
  if (!response.ok) {
811
- throw new Error(`Ollama API error: ${response.status}`);
1087
+ const errorText = await response.text().catch(() => "Unknown error");
1088
+ throw new Error(`Ollama API error: ${response.status} - ${errorText}`);
812
1089
  }
813
1090
  if (!response.body) {
814
1091
  throw new Error("No response body");
@@ -816,6 +1093,8 @@ var OllamaAdapter = class {
816
1093
  const reader = response.body.getReader();
817
1094
  const decoder = new TextDecoder();
818
1095
  let buffer = "";
1096
+ let hasEmittedToolCalls = false;
1097
+ const toolCallsInProgress = /* @__PURE__ */ new Map();
819
1098
  while (true) {
820
1099
  const { done, value } = await reader.read();
821
1100
  if (done) break;
@@ -829,6 +1108,28 @@ var OllamaAdapter = class {
829
1108
  if (chunk.message?.content) {
830
1109
  yield { type: "message:delta", content: chunk.message.content };
831
1110
  }
1111
+ if (chunk.message?.tool_calls && !hasEmittedToolCalls) {
1112
+ for (let i = 0; i < chunk.message.tool_calls.length; i++) {
1113
+ const toolCall = chunk.message.tool_calls[i];
1114
+ const toolCallId = toolCall.id || generateToolCallId();
1115
+ toolCallsInProgress.set(i, {
1116
+ id: toolCallId,
1117
+ name: toolCall.function?.name || "",
1118
+ arguments: toolCall.function?.arguments || {}
1119
+ });
1120
+ yield {
1121
+ type: "action:start",
1122
+ id: toolCallId,
1123
+ name: toolCall.function?.name || ""
1124
+ };
1125
+ yield {
1126
+ type: "action:args",
1127
+ id: toolCallId,
1128
+ args: JSON.stringify(toolCall.function?.arguments || {})
1129
+ };
1130
+ }
1131
+ hasEmittedToolCalls = true;
1132
+ }
832
1133
  if (chunk.done) {
833
1134
  break;
834
1135
  }
@@ -842,10 +1143,20 @@ var OllamaAdapter = class {
842
1143
  if (error.name === "AbortError") {
843
1144
  yield { type: "done" };
844
1145
  } else {
1146
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
1147
+ let code = "OLLAMA_ERROR";
1148
+ let message = errorMessage;
1149
+ if (errorMessage.includes("ECONNREFUSED") || errorMessage.includes("fetch failed")) {
1150
+ message = `Cannot connect to Ollama at ${this.baseUrl}. Make sure Ollama is running (ollama serve).`;
1151
+ code = "OLLAMA_CONNECTION_ERROR";
1152
+ } else if (errorMessage.includes("model") && errorMessage.includes("not found")) {
1153
+ message = `Model "${this.model}" not found. Pull it with: ollama pull ${this.model}`;
1154
+ code = "OLLAMA_MODEL_NOT_FOUND";
1155
+ }
845
1156
  yield {
846
1157
  type: "error",
847
- message: error instanceof Error ? error.message : "Unknown error",
848
- code: "OLLAMA_ERROR"
1158
+ message,
1159
+ code
849
1160
  };
850
1161
  }
851
1162
  }
@@ -986,6 +1297,7 @@ var GoogleAdapter = class {
986
1297
  async *stream(request) {
987
1298
  const client = await this.getClient();
988
1299
  const modelId = request.config?.model || this.model;
1300
+ const webSearchConfig = request.webSearch ?? this.config.webSearch;
989
1301
  const model = client.getGenerativeModel({
990
1302
  model: modelId,
991
1303
  safetySettings: this.config.safetySettings
@@ -1033,7 +1345,16 @@ var GoogleAdapter = class {
1033
1345
  mergedContents.push({ ...content, parts: [...content.parts] });
1034
1346
  }
1035
1347
  }
1036
- const tools = formatToolsForGemini(request.actions);
1348
+ const functionTools = formatToolsForGemini(request.actions);
1349
+ const toolsArray = [];
1350
+ if (functionTools) {
1351
+ toolsArray.push(functionTools);
1352
+ }
1353
+ if (webSearchConfig) {
1354
+ toolsArray.push({
1355
+ google_search: {}
1356
+ });
1357
+ }
1037
1358
  const messageId = generateMessageId();
1038
1359
  yield { type: "message:start", id: messageId };
1039
1360
  try {
@@ -1041,7 +1362,7 @@ var GoogleAdapter = class {
1041
1362
  history: mergedContents.slice(0, -1),
1042
1363
  // All but the last message
1043
1364
  systemInstruction: systemInstruction ? { parts: [{ text: systemInstruction }] } : void 0,
1044
- tools: tools ? [tools] : void 0,
1365
+ tools: toolsArray.length > 0 ? toolsArray : void 0,
1045
1366
  generationConfig: {
1046
1367
  temperature: request.config?.temperature ?? this.config.temperature,
1047
1368
  maxOutputTokens: request.config?.maxTokens ?? this.config.maxTokens
@@ -1050,6 +1371,7 @@ var GoogleAdapter = class {
1050
1371
  const lastMessage = mergedContents[mergedContents.length - 1];
1051
1372
  const result = await chat.sendMessageStream(lastMessage.parts);
1052
1373
  let currentToolCall = null;
1374
+ const collectedCitations = [];
1053
1375
  for await (const chunk of result.stream) {
1054
1376
  if (request.signal?.aborted) {
1055
1377
  break;
@@ -1091,6 +1413,24 @@ var GoogleAdapter = class {
1091
1413
  };
1092
1414
  }
1093
1415
  }
1416
+ const groundingMetadata = candidate?.groundingMetadata;
1417
+ if (groundingMetadata?.groundingChunks) {
1418
+ for (const chunk2 of groundingMetadata.groundingChunks) {
1419
+ if (chunk2.web?.uri) {
1420
+ const url = chunk2.web.uri;
1421
+ const domain = extractDomain3(url);
1422
+ if (!collectedCitations.some((c) => c.url === url)) {
1423
+ collectedCitations.push({
1424
+ index: collectedCitations.length + 1,
1425
+ url,
1426
+ title: chunk2.web.title || domain,
1427
+ domain,
1428
+ favicon: domain ? `https://www.google.com/s2/favicons?domain=${domain}&sz=32` : void 0
1429
+ });
1430
+ }
1431
+ }
1432
+ }
1433
+ }
1094
1434
  }
1095
1435
  let usage;
1096
1436
  try {
@@ -1102,8 +1442,30 @@ var GoogleAdapter = class {
1102
1442
  total_tokens: response.usageMetadata.totalTokenCount || 0
1103
1443
  };
1104
1444
  }
1445
+ const finalCandidate = response.candidates?.[0];
1446
+ const finalGrounding = finalCandidate?.groundingMetadata;
1447
+ if (finalGrounding?.groundingChunks) {
1448
+ for (const chunk of finalGrounding.groundingChunks) {
1449
+ if (chunk.web?.uri) {
1450
+ const url = chunk.web.uri;
1451
+ const domain = extractDomain3(url);
1452
+ if (!collectedCitations.some((c) => c.url === url)) {
1453
+ collectedCitations.push({
1454
+ index: collectedCitations.length + 1,
1455
+ url,
1456
+ title: chunk.web.title || domain,
1457
+ domain,
1458
+ favicon: domain ? `https://www.google.com/s2/favicons?domain=${domain}&sz=32` : void 0
1459
+ });
1460
+ }
1461
+ }
1462
+ }
1463
+ }
1105
1464
  } catch {
1106
1465
  }
1466
+ if (collectedCitations.length > 0) {
1467
+ yield { type: "citation", citations: collectedCitations };
1468
+ }
1107
1469
  yield { type: "message:end" };
1108
1470
  yield { type: "done", usage };
1109
1471
  } catch (error) {
@@ -1191,6 +1553,14 @@ var GoogleAdapter = class {
1191
1553
  function createGoogleAdapter(config) {
1192
1554
  return new GoogleAdapter(config);
1193
1555
  }
1556
+ function extractDomain3(url) {
1557
+ try {
1558
+ const parsed = new URL(url);
1559
+ return parsed.hostname;
1560
+ } catch {
1561
+ return "";
1562
+ }
1563
+ }
1194
1564
 
1195
1565
  // src/adapters/xai.ts
1196
1566
  var XAI_BASE_URL = "https://api.x.ai/v1";