@x12i/openrouter-runtime 1.0.0 → 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.
package/dist/index.cjs CHANGED
@@ -191,7 +191,7 @@ function isRecord(value) {
191
191
  function resultToChatToolMessage(result) {
192
192
  return {
193
193
  role: "tool",
194
- toolCallId: result.callId,
194
+ tool_call_id: result.callId,
195
195
  name: result.name,
196
196
  content: stringifyToolResult(result.status === "completed" ? result.result : { error: result.error })
197
197
  };
@@ -207,6 +207,9 @@ function resultToResponsesInput(result) {
207
207
  // src/normalize/citations.ts
208
208
  function extractCitations(value) {
209
209
  const citations = [];
210
+ if (typeof value === "string") {
211
+ return extractCitationsFromText(value);
212
+ }
210
213
  visit(value, (item) => {
211
214
  const url = pickString(item, ["url", "uri"]);
212
215
  if (!url) return;
@@ -221,7 +224,37 @@ function extractCitations(value) {
221
224
  ...optional("endIndex", pickNumber(item, ["end_index", "endIndex"]))
222
225
  });
223
226
  });
224
- return dedupe(citations);
227
+ return dedupeCitations(citations);
228
+ }
229
+ function extractCitationsFromText(text) {
230
+ const citations = [];
231
+ const markdownLinkPattern = /\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g;
232
+ for (const match of text.matchAll(markdownLinkPattern)) {
233
+ const title = match[1];
234
+ const url = cleanUrl(match[2]);
235
+ const startIndex = match.index;
236
+ citations.push({
237
+ type: "url",
238
+ url,
239
+ sourceProvider: "openrouter",
240
+ raw: match[0],
241
+ ...title ? { title } : {},
242
+ ...startIndex !== void 0 ? { startIndex, endIndex: startIndex + match[0].length } : {}
243
+ });
244
+ }
245
+ const urlPattern = /https?:\/\/[^\s)]+/g;
246
+ for (const match of text.matchAll(urlPattern)) {
247
+ const url = cleanUrl(match[0]);
248
+ const startIndex = match.index;
249
+ citations.push({
250
+ type: "url",
251
+ url,
252
+ sourceProvider: "openrouter",
253
+ raw: match[0],
254
+ ...startIndex !== void 0 ? { startIndex, endIndex: startIndex + match[0].length } : {}
255
+ });
256
+ }
257
+ return dedupeCitations(citations);
225
258
  }
226
259
  function optional(key, value) {
227
260
  return value === void 0 ? {} : { [key]: value };
@@ -243,15 +276,18 @@ function pickNumber(record, keys) {
243
276
  for (const key of keys) if (typeof record[key] === "number") return record[key];
244
277
  return void 0;
245
278
  }
246
- function dedupe(citations) {
279
+ function dedupeCitations(citations) {
247
280
  const seen = /* @__PURE__ */ new Set();
248
281
  return citations.filter((citation) => {
249
- const key = `${citation.url}:${citation.startIndex ?? ""}:${citation.endIndex ?? ""}`;
282
+ const key = citation.url;
250
283
  if (seen.has(key)) return false;
251
284
  seen.add(key);
252
285
  return true;
253
286
  });
254
287
  }
288
+ function cleanUrl(url) {
289
+ return (url ?? "").replace(/[.,;:!?]+$/, "");
290
+ }
255
291
 
256
292
  // src/normalize/images.ts
257
293
  function extractImages(value) {
@@ -433,7 +469,10 @@ function normalizeChatResponse(params) {
433
469
  apiMode: params.apiMode,
434
470
  model: typeof responseRecord.model === "string" ? responseRecord.model : params.model,
435
471
  text,
436
- citations: extractCitations([message.annotations, responseRecord.citations, params.response]),
472
+ citations: dedupeCitations([
473
+ ...extractCitations([message.annotations, responseRecord.citations, params.response]),
474
+ ...extractCitations(text)
475
+ ]),
437
476
  images: extractImages(params.response),
438
477
  patches: extractPatches(params.response),
439
478
  toolUsage: createToolUsage(params.serverTools, params.response, params.functionToolUsage),
@@ -466,6 +505,10 @@ function optional3(key, value) {
466
505
  async function validatePostResponse(response, defaults) {
467
506
  const errors = [...response.errors];
468
507
  const warnings = [...response.warnings];
508
+ if (response.toolUsage.serverTools.webSearch.requested && response.citations.length > 0 && !response.toolUsage.serverTools.webSearch.used) {
509
+ response.toolUsage.serverTools.webSearch.used = true;
510
+ response.toolUsage.serverTools.webSearch.callCount = Math.max(response.toolUsage.serverTools.webSearch.callCount ?? 0, 1);
511
+ }
469
512
  for (const [key, usage] of Object.entries(response.toolUsage.serverTools)) {
470
513
  if (usage.required && !usage.used) {
471
514
  const code = `${toScreamingSnake(key)}_REQUIRED_BUT_NOT_USED`;
@@ -862,7 +905,7 @@ function compileMessages(request) {
862
905
  if (request.system) messages.push({ role: "system", content: request.system });
863
906
  if (request.messages) messages.push(...request.messages);
864
907
  if (request.prompt) messages.push({ role: "user", content: request.prompt });
865
- return messages;
908
+ return messages.map(compileMessage);
866
909
  }
867
910
  function compilePromptInput(request) {
868
911
  if (request.prompt) return request.prompt;
@@ -871,6 +914,32 @@ function compilePromptInput(request) {
871
914
  content: message.content
872
915
  }));
873
916
  }
917
+ function compileMessage(message) {
918
+ return clean({
919
+ role: message.role,
920
+ content: compileContent(message.content),
921
+ name: message.name,
922
+ tool_call_id: message.toolCallId
923
+ });
924
+ }
925
+ function compileContent(content) {
926
+ if (typeof content === "string") return content;
927
+ return content.map((part) => {
928
+ if (part.type === "image_url") {
929
+ return { type: "image_url", image_url: { url: part.imageUrl } };
930
+ }
931
+ if (part.type === "file") {
932
+ return clean({
933
+ type: "file",
934
+ file_name: part.fileName,
935
+ mime_type: part.mimeType,
936
+ data: part.data,
937
+ url: part.url
938
+ });
939
+ }
940
+ return part;
941
+ });
942
+ }
874
943
  function compileToolChoice(choice, serverTools) {
875
944
  if (!choice || choice === "auto") return void 0;
876
945
  if (choice === "none" || choice === "required") return choice;
@@ -998,7 +1067,7 @@ async function executeFunctionCalls(params) {
998
1067
  name: result.name,
999
1068
  callId: result.callId,
1000
1069
  status: result.status,
1001
- args: call.args,
1070
+ args: parseToolArgs(call.args),
1002
1071
  ...result.result !== void 0 ? { result: result.result } : {},
1003
1072
  ...result.error !== void 0 ? { error: result.error } : {},
1004
1073
  ...result.durationMs !== void 0 ? { durationMs: result.durationMs } : {}
@@ -1034,8 +1103,10 @@ async function runChat(request, compiled, context) {
1034
1103
  functionUsage.push(...results);
1035
1104
  const body = activeCompiled.body;
1036
1105
  const messages = Array.isArray(body.messages) ? [...body.messages] : [];
1106
+ const assistantMessage = extractAssistantMessage(response);
1107
+ if (assistantMessage) messages.push(assistantMessage);
1037
1108
  messages.push(...results.map(resultToChatToolMessage));
1038
- activeCompiled = { ...activeCompiled, body: { ...body, messages } };
1109
+ activeCompiled = { ...activeCompiled, body: { ...body, messages, tool_choice: "auto" } };
1039
1110
  }
1040
1111
  const model = typeof compiled.body.model === "string" ? compiled.body.model : "";
1041
1112
  const normalized = normalizeChatResponse({
@@ -1053,17 +1124,25 @@ async function runChat(request, compiled, context) {
1053
1124
  function optional4(key, value) {
1054
1125
  return value === void 0 ? {} : { [key]: value };
1055
1126
  }
1127
+ function extractAssistantMessage(response) {
1128
+ const record = isRecord(response) ? response : {};
1129
+ const choice = Array.isArray(record.choices) && isRecord(record.choices[0]) ? record.choices[0] : {};
1130
+ const message = isRecord(choice.message) ? choice.message : void 0;
1131
+ if (!message) return void 0;
1132
+ return message;
1133
+ }
1056
1134
 
1057
1135
  // src/normalize/normalize-responses-response.ts
1058
1136
  function normalizeResponsesResponse(params) {
1059
1137
  const record = isRecord(params.response) ? params.response : {};
1138
+ const text = extractOutputText(record);
1060
1139
  return {
1061
1140
  id: typeof record.id === "string" ? record.id : createRuntimeId("orresp"),
1062
1141
  status: record.status === "requires_action" ? "requires_action" : "completed",
1063
1142
  apiMode: "responses",
1064
1143
  model: typeof record.model === "string" ? record.model : params.model,
1065
- text: extractOutputText(record),
1066
- citations: extractCitations(params.response),
1144
+ text,
1145
+ citations: dedupeCitations([...extractCitations(params.response), ...extractCitations(text)]),
1067
1146
  images: extractImages(params.response),
1068
1147
  patches: extractPatches(params.response),
1069
1148
  toolUsage: createToolUsage(params.serverTools, params.response, params.functionToolUsage),
@@ -1125,6 +1204,7 @@ async function runResponses(request, compiled, context) {
1125
1204
  ...activeCompiled,
1126
1205
  body: {
1127
1206
  ...activeCompiled.body,
1207
+ tool_choice: "auto",
1128
1208
  previous_response_id: isRecord(response) && typeof response.id === "string" ? response.id : void 0,
1129
1209
  input: results.map(resultToResponsesInput)
1130
1210
  }