@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.js CHANGED
@@ -161,7 +161,7 @@ function isRecord(value) {
161
161
  function resultToChatToolMessage(result) {
162
162
  return {
163
163
  role: "tool",
164
- toolCallId: result.callId,
164
+ tool_call_id: result.callId,
165
165
  name: result.name,
166
166
  content: stringifyToolResult(result.status === "completed" ? result.result : { error: result.error })
167
167
  };
@@ -177,6 +177,9 @@ function resultToResponsesInput(result) {
177
177
  // src/normalize/citations.ts
178
178
  function extractCitations(value) {
179
179
  const citations = [];
180
+ if (typeof value === "string") {
181
+ return extractCitationsFromText(value);
182
+ }
180
183
  visit(value, (item) => {
181
184
  const url = pickString(item, ["url", "uri"]);
182
185
  if (!url) return;
@@ -191,7 +194,37 @@ function extractCitations(value) {
191
194
  ...optional("endIndex", pickNumber(item, ["end_index", "endIndex"]))
192
195
  });
193
196
  });
194
- return dedupe(citations);
197
+ return dedupeCitations(citations);
198
+ }
199
+ function extractCitationsFromText(text) {
200
+ const citations = [];
201
+ const markdownLinkPattern = /\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g;
202
+ for (const match of text.matchAll(markdownLinkPattern)) {
203
+ const title = match[1];
204
+ const url = cleanUrl(match[2]);
205
+ const startIndex = match.index;
206
+ citations.push({
207
+ type: "url",
208
+ url,
209
+ sourceProvider: "openrouter",
210
+ raw: match[0],
211
+ ...title ? { title } : {},
212
+ ...startIndex !== void 0 ? { startIndex, endIndex: startIndex + match[0].length } : {}
213
+ });
214
+ }
215
+ const urlPattern = /https?:\/\/[^\s)]+/g;
216
+ for (const match of text.matchAll(urlPattern)) {
217
+ const url = cleanUrl(match[0]);
218
+ const startIndex = match.index;
219
+ citations.push({
220
+ type: "url",
221
+ url,
222
+ sourceProvider: "openrouter",
223
+ raw: match[0],
224
+ ...startIndex !== void 0 ? { startIndex, endIndex: startIndex + match[0].length } : {}
225
+ });
226
+ }
227
+ return dedupeCitations(citations);
195
228
  }
196
229
  function optional(key, value) {
197
230
  return value === void 0 ? {} : { [key]: value };
@@ -213,15 +246,18 @@ function pickNumber(record, keys) {
213
246
  for (const key of keys) if (typeof record[key] === "number") return record[key];
214
247
  return void 0;
215
248
  }
216
- function dedupe(citations) {
249
+ function dedupeCitations(citations) {
217
250
  const seen = /* @__PURE__ */ new Set();
218
251
  return citations.filter((citation) => {
219
- const key = `${citation.url}:${citation.startIndex ?? ""}:${citation.endIndex ?? ""}`;
252
+ const key = citation.url;
220
253
  if (seen.has(key)) return false;
221
254
  seen.add(key);
222
255
  return true;
223
256
  });
224
257
  }
258
+ function cleanUrl(url) {
259
+ return (url ?? "").replace(/[.,;:!?]+$/, "");
260
+ }
225
261
 
226
262
  // src/normalize/images.ts
227
263
  function extractImages(value) {
@@ -403,7 +439,10 @@ function normalizeChatResponse(params) {
403
439
  apiMode: params.apiMode,
404
440
  model: typeof responseRecord.model === "string" ? responseRecord.model : params.model,
405
441
  text,
406
- citations: extractCitations([message.annotations, responseRecord.citations, params.response]),
442
+ citations: dedupeCitations([
443
+ ...extractCitations([message.annotations, responseRecord.citations, params.response]),
444
+ ...extractCitations(text)
445
+ ]),
407
446
  images: extractImages(params.response),
408
447
  patches: extractPatches(params.response),
409
448
  toolUsage: createToolUsage(params.serverTools, params.response, params.functionToolUsage),
@@ -436,6 +475,10 @@ function optional3(key, value) {
436
475
  async function validatePostResponse(response, defaults) {
437
476
  const errors = [...response.errors];
438
477
  const warnings = [...response.warnings];
478
+ if (response.toolUsage.serverTools.webSearch.requested && response.citations.length > 0 && !response.toolUsage.serverTools.webSearch.used) {
479
+ response.toolUsage.serverTools.webSearch.used = true;
480
+ response.toolUsage.serverTools.webSearch.callCount = Math.max(response.toolUsage.serverTools.webSearch.callCount ?? 0, 1);
481
+ }
439
482
  for (const [key, usage] of Object.entries(response.toolUsage.serverTools)) {
440
483
  if (usage.required && !usage.used) {
441
484
  const code = `${toScreamingSnake(key)}_REQUIRED_BUT_NOT_USED`;
@@ -832,7 +875,7 @@ function compileMessages(request) {
832
875
  if (request.system) messages.push({ role: "system", content: request.system });
833
876
  if (request.messages) messages.push(...request.messages);
834
877
  if (request.prompt) messages.push({ role: "user", content: request.prompt });
835
- return messages;
878
+ return messages.map(compileMessage);
836
879
  }
837
880
  function compilePromptInput(request) {
838
881
  if (request.prompt) return request.prompt;
@@ -841,6 +884,32 @@ function compilePromptInput(request) {
841
884
  content: message.content
842
885
  }));
843
886
  }
887
+ function compileMessage(message) {
888
+ return clean({
889
+ role: message.role,
890
+ content: compileContent(message.content),
891
+ name: message.name,
892
+ tool_call_id: message.toolCallId
893
+ });
894
+ }
895
+ function compileContent(content) {
896
+ if (typeof content === "string") return content;
897
+ return content.map((part) => {
898
+ if (part.type === "image_url") {
899
+ return { type: "image_url", image_url: { url: part.imageUrl } };
900
+ }
901
+ if (part.type === "file") {
902
+ return clean({
903
+ type: "file",
904
+ file_name: part.fileName,
905
+ mime_type: part.mimeType,
906
+ data: part.data,
907
+ url: part.url
908
+ });
909
+ }
910
+ return part;
911
+ });
912
+ }
844
913
  function compileToolChoice(choice, serverTools) {
845
914
  if (!choice || choice === "auto") return void 0;
846
915
  if (choice === "none" || choice === "required") return choice;
@@ -968,7 +1037,7 @@ async function executeFunctionCalls(params) {
968
1037
  name: result.name,
969
1038
  callId: result.callId,
970
1039
  status: result.status,
971
- args: call.args,
1040
+ args: parseToolArgs(call.args),
972
1041
  ...result.result !== void 0 ? { result: result.result } : {},
973
1042
  ...result.error !== void 0 ? { error: result.error } : {},
974
1043
  ...result.durationMs !== void 0 ? { durationMs: result.durationMs } : {}
@@ -1004,8 +1073,10 @@ async function runChat(request, compiled, context) {
1004
1073
  functionUsage.push(...results);
1005
1074
  const body = activeCompiled.body;
1006
1075
  const messages = Array.isArray(body.messages) ? [...body.messages] : [];
1076
+ const assistantMessage = extractAssistantMessage(response);
1077
+ if (assistantMessage) messages.push(assistantMessage);
1007
1078
  messages.push(...results.map(resultToChatToolMessage));
1008
- activeCompiled = { ...activeCompiled, body: { ...body, messages } };
1079
+ activeCompiled = { ...activeCompiled, body: { ...body, messages, tool_choice: "auto" } };
1009
1080
  }
1010
1081
  const model = typeof compiled.body.model === "string" ? compiled.body.model : "";
1011
1082
  const normalized = normalizeChatResponse({
@@ -1023,17 +1094,25 @@ async function runChat(request, compiled, context) {
1023
1094
  function optional4(key, value) {
1024
1095
  return value === void 0 ? {} : { [key]: value };
1025
1096
  }
1097
+ function extractAssistantMessage(response) {
1098
+ const record = isRecord(response) ? response : {};
1099
+ const choice = Array.isArray(record.choices) && isRecord(record.choices[0]) ? record.choices[0] : {};
1100
+ const message = isRecord(choice.message) ? choice.message : void 0;
1101
+ if (!message) return void 0;
1102
+ return message;
1103
+ }
1026
1104
 
1027
1105
  // src/normalize/normalize-responses-response.ts
1028
1106
  function normalizeResponsesResponse(params) {
1029
1107
  const record = isRecord(params.response) ? params.response : {};
1108
+ const text = extractOutputText(record);
1030
1109
  return {
1031
1110
  id: typeof record.id === "string" ? record.id : createRuntimeId("orresp"),
1032
1111
  status: record.status === "requires_action" ? "requires_action" : "completed",
1033
1112
  apiMode: "responses",
1034
1113
  model: typeof record.model === "string" ? record.model : params.model,
1035
- text: extractOutputText(record),
1036
- citations: extractCitations(params.response),
1114
+ text,
1115
+ citations: dedupeCitations([...extractCitations(params.response), ...extractCitations(text)]),
1037
1116
  images: extractImages(params.response),
1038
1117
  patches: extractPatches(params.response),
1039
1118
  toolUsage: createToolUsage(params.serverTools, params.response, params.functionToolUsage),
@@ -1095,6 +1174,7 @@ async function runResponses(request, compiled, context) {
1095
1174
  ...activeCompiled,
1096
1175
  body: {
1097
1176
  ...activeCompiled.body,
1177
+ tool_choice: "auto",
1098
1178
  previous_response_id: isRecord(response) && typeof response.id === "string" ? response.id : void 0,
1099
1179
  input: results.map(resultToResponsesInput)
1100
1180
  }