@agentfield/sdk 0.1.91 → 0.1.92-rc.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.d.ts CHANGED
@@ -45,6 +45,7 @@ declare class AIClient {
45
45
  getModel(options?: AIRequestOptions): _ai_sdk_provider.LanguageModelV3;
46
46
  private buildModel;
47
47
  private buildEmbeddingModel;
48
+ private openRouterHeaders;
48
49
  private getRateLimiter;
49
50
  private withRateLimitRetry;
50
51
  }
@@ -869,6 +870,9 @@ interface AIConfig {
869
870
  embeddingModel?: string;
870
871
  apiKey?: string;
871
872
  baseUrl?: string;
873
+ openRouterSiteUrl?: string;
874
+ openRouterAppName?: string;
875
+ openRouterHeaders?: Record<string, string>;
872
876
  temperature?: number;
873
877
  maxTokens?: number;
874
878
  enableRateLimitRetry?: boolean;
@@ -1780,11 +1784,15 @@ declare class MediaRouter {
1780
1784
  interface OpenRouterMediaProviderOptions {
1781
1785
  apiKey?: string;
1782
1786
  baseUrl?: string;
1787
+ openRouterSiteUrl?: string;
1788
+ openRouterAppName?: string;
1789
+ openRouterHeaders?: Record<string, string>;
1783
1790
  }
1784
1791
  declare class OpenRouterMediaProvider implements MediaProvider {
1785
1792
  readonly name = "openrouter";
1786
1793
  readonly supportedModalities: string[];
1787
1794
  private readonly baseUrl;
1795
+ private readonly attributionHeaders;
1788
1796
  constructor(options?: OpenRouterMediaProviderOptions);
1789
1797
  /**
1790
1798
  * Seed the metadata cache for a model. Useful when running against test
package/dist/index.js CHANGED
@@ -34,6 +34,107 @@ var __export = (target, all) => {
34
34
  for (var name in all)
35
35
  __defProp(target, name, { get: all[name], enumerable: true });
36
36
  };
37
+
38
+ // src/ai/openrouterAttribution.ts
39
+ function isOpenRouterRequest(options) {
40
+ const provider = clean(options.provider)?.toLowerCase();
41
+ if (provider === "openrouter") {
42
+ return true;
43
+ }
44
+ const model = clean(options.model)?.toLowerCase();
45
+ if (model?.startsWith("openrouter/")) {
46
+ return true;
47
+ }
48
+ const baseUrl = clean(options.baseUrl)?.toLowerCase();
49
+ return Boolean(baseUrl?.includes("openrouter.ai"));
50
+ }
51
+ function openRouterAttributionEnabled(env = process.env) {
52
+ const raw = clean(env.AGENTFIELD_OPENROUTER_ATTRIBUTION);
53
+ if (!raw) {
54
+ return true;
55
+ }
56
+ return !["0", "false", "no", "off"].includes(raw.toLowerCase());
57
+ }
58
+ function resolveOpenRouterAttribution(options = {}) {
59
+ const env = options.env ?? process.env;
60
+ if (!openRouterAttributionEnabled(env)) {
61
+ return void 0;
62
+ }
63
+ const siteUrl = clean(options.siteUrl) ?? clean(env.AGENTFIELD_OPENROUTER_SITE_URL) ?? clean(env.OR_SITE_URL) ?? DEFAULT_OPENROUTER_SITE_URL;
64
+ const appName = clean(options.appName) ?? clean(env.AGENTFIELD_OPENROUTER_APP_NAME) ?? clean(env.OR_APP_NAME) ?? DEFAULT_OPENROUTER_APP_NAME;
65
+ return { siteUrl, appName };
66
+ }
67
+ function openRouterAttributionHeaders(options = {}) {
68
+ const resolved = resolveOpenRouterAttribution(options);
69
+ if (!resolved) {
70
+ return {};
71
+ }
72
+ return {
73
+ "HTTP-Referer": resolved.siteUrl,
74
+ "X-OpenRouter-Title": resolved.appName,
75
+ "X-Title": resolved.appName
76
+ };
77
+ }
78
+ function mergeOpenRouterAttributionHeaders(existing = {}, options = {}) {
79
+ const merged = {};
80
+ const lowerKeys = /* @__PURE__ */ new Set();
81
+ for (const [key, value] of Object.entries(existing)) {
82
+ const cleaned = clean(value);
83
+ if (!cleaned) {
84
+ continue;
85
+ }
86
+ merged[key] = cleaned;
87
+ lowerKeys.add(key.toLowerCase());
88
+ }
89
+ for (const [key, value] of Object.entries(openRouterAttributionHeaders(options))) {
90
+ if (!lowerKeys.has(key.toLowerCase())) {
91
+ merged[key] = value;
92
+ }
93
+ }
94
+ return merged;
95
+ }
96
+ function openRouterAttributionEnv(env = process.env) {
97
+ const resolved = resolveOpenRouterAttribution({ env });
98
+ if (!resolved) {
99
+ return {};
100
+ }
101
+ return {
102
+ AGENTFIELD_OPENROUTER_SITE_URL: resolved.siteUrl,
103
+ AGENTFIELD_OPENROUTER_APP_NAME: resolved.appName,
104
+ OR_SITE_URL: resolved.siteUrl,
105
+ OR_APP_NAME: resolved.appName
106
+ };
107
+ }
108
+ function applyOpenRouterAttributionEnv(env) {
109
+ if (!openRouterAttributionEnabled(env)) {
110
+ for (const key of [
111
+ "AGENTFIELD_OPENROUTER_SITE_URL",
112
+ "AGENTFIELD_OPENROUTER_APP_NAME",
113
+ "OR_SITE_URL",
114
+ "OR_APP_NAME"
115
+ ]) {
116
+ delete env[key];
117
+ }
118
+ return;
119
+ }
120
+ const attribution = openRouterAttributionEnv(env);
121
+ for (const [key, value] of Object.entries(attribution)) {
122
+ if (clean(env[key]) == null) {
123
+ env[key] = value;
124
+ }
125
+ }
126
+ }
127
+ function clean(value) {
128
+ const trimmed = value?.trim();
129
+ return trimmed ? trimmed : void 0;
130
+ }
131
+ var DEFAULT_OPENROUTER_SITE_URL, DEFAULT_OPENROUTER_APP_NAME;
132
+ var init_openrouterAttribution = __esm({
133
+ "src/ai/openrouterAttribution.ts"() {
134
+ DEFAULT_OPENROUTER_SITE_URL = "https://agentfield.ai";
135
+ DEFAULT_OPENROUTER_APP_NAME = "AgentField AI";
136
+ }
137
+ });
37
138
  function getZodConverter() {
38
139
  if (zodConverter !== void 0) {
39
140
  return zodConverter;
@@ -340,8 +441,10 @@ var init_claude = __esm({
340
441
  function runCli(cmd, options) {
341
442
  return new Promise((resolve2, reject) => {
342
443
  const [bin, ...args] = cmd;
444
+ const env = { ...process.env, ...options?.env };
445
+ applyOpenRouterAttributionEnv(env);
343
446
  const proc = spawn(bin, args, {
344
- env: { ...process.env, ...options?.env },
447
+ env,
345
448
  cwd: options?.cwd,
346
449
  stdio: ["pipe", "pipe", "pipe"]
347
450
  });
@@ -414,6 +517,7 @@ function extractFinalText(events) {
414
517
  }
415
518
  var init_cli = __esm({
416
519
  "src/harness/cli.ts"() {
520
+ init_openrouterAttribution();
417
521
  }
418
522
  });
419
523
 
@@ -566,6 +670,7 @@ var init_opencode = __esm({
566
670
  "src/harness/providers/opencode.ts"() {
567
671
  init_types();
568
672
  init_cli();
673
+ init_openrouterAttribution();
569
674
  OpenCodeProvider = class {
570
675
  bin;
571
676
  constructor(binPath = "opencode") {
@@ -593,6 +698,22 @@ USER REQUEST:
593
698
  ${prompt}`;
594
699
  }
595
700
  cmd.push(effectivePrompt);
701
+ const explicitModel = typeof options.model === "string" ? options.model : void 0;
702
+ if (explicitModel && isOpenRouterRequest({ model: explicitModel }) && !env.OPENCODE_CONFIG_CONTENT && !process.env.OPENCODE_CONFIG_CONTENT) {
703
+ const modelSlug = explicitModel.slice("openrouter/".length);
704
+ const headers = openRouterAttributionHeaders({ env: { ...process.env, ...env } });
705
+ if (modelSlug && Object.keys(headers).length > 0) {
706
+ env.OPENCODE_CONFIG_CONTENT = JSON.stringify({
707
+ provider: {
708
+ openrouter: {
709
+ models: {
710
+ [modelSlug]: { headers }
711
+ }
712
+ }
713
+ }
714
+ });
715
+ }
716
+ }
596
717
  const startApi = Date.now();
597
718
  try {
598
719
  const { stdout, stderr, exitCode } = await runCli(cmd, { env });
@@ -1671,6 +1792,7 @@ function toError(error) {
1671
1792
  }
1672
1793
 
1673
1794
  // src/ai/AIClient.ts
1795
+ init_openrouterAttribution();
1674
1796
  function repairJsonText(text2) {
1675
1797
  let cleaned = text2.trim();
1676
1798
  const codeBlockMatch = cleaned.match(/```(?:json)?\s*([\s\S]*?)```/);
@@ -1772,6 +1894,7 @@ var AIClient = class {
1772
1894
  buildModel(options) {
1773
1895
  const provider = options.provider ?? this.config.provider ?? "openai";
1774
1896
  const modelName = options.model ?? this.config.model ?? "gpt-4o";
1897
+ const openRouterHeaders = this.openRouterHeaders(provider, modelName);
1775
1898
  switch (provider) {
1776
1899
  case "anthropic": {
1777
1900
  const anthropic = createAnthropic({
@@ -1825,7 +1948,8 @@ var AIClient = class {
1825
1948
  case "openrouter": {
1826
1949
  const openrouter = createOpenAI({
1827
1950
  apiKey: this.config.apiKey,
1828
- baseURL: this.config.baseUrl ?? "https://openrouter.ai/api/v1"
1951
+ baseURL: this.config.baseUrl ?? "https://openrouter.ai/api/v1",
1952
+ headers: openRouterHeaders
1829
1953
  });
1830
1954
  return openrouter.chat(modelName);
1831
1955
  }
@@ -1841,7 +1965,8 @@ var AIClient = class {
1841
1965
  default: {
1842
1966
  const openai = createOpenAI({
1843
1967
  apiKey: this.config.apiKey,
1844
- baseURL: this.config.baseUrl
1968
+ baseURL: this.config.baseUrl,
1969
+ ...openRouterHeaders ? { headers: openRouterHeaders } : {}
1845
1970
  });
1846
1971
  return openai(modelName);
1847
1972
  }
@@ -1850,6 +1975,7 @@ var AIClient = class {
1850
1975
  buildEmbeddingModel(options) {
1851
1976
  const provider = options.provider ?? this.config.provider ?? "openai";
1852
1977
  const modelName = options.model ?? this.config.embeddingModel ?? "text-embedding-3-small";
1978
+ const openRouterHeaders = this.openRouterHeaders(provider, modelName);
1853
1979
  const noEmbeddingProviders = ["anthropic", "xai", "deepseek", "groq"];
1854
1980
  if (noEmbeddingProviders.includes(provider)) {
1855
1981
  throw new Error(`Embedding generation is not supported for ${provider} provider`);
@@ -1882,12 +2008,22 @@ var AIClient = class {
1882
2008
  default: {
1883
2009
  const openai = createOpenAI({
1884
2010
  apiKey: this.config.apiKey ?? (provider === "ollama" ? "ollama" : void 0),
1885
- baseURL: this.config.baseUrl ?? (provider === "openrouter" ? "https://openrouter.ai/api/v1" : provider === "ollama" ? "http://localhost:11434/v1" : void 0)
2011
+ baseURL: this.config.baseUrl ?? (provider === "openrouter" ? "https://openrouter.ai/api/v1" : provider === "ollama" ? "http://localhost:11434/v1" : void 0),
2012
+ ...openRouterHeaders ? { headers: openRouterHeaders } : {}
1886
2013
  });
1887
2014
  return openai.embedding(modelName);
1888
2015
  }
1889
2016
  }
1890
2017
  }
2018
+ openRouterHeaders(provider, model) {
2019
+ if (!isOpenRouterRequest({ provider, model, baseUrl: this.config.baseUrl })) {
2020
+ return void 0;
2021
+ }
2022
+ return mergeOpenRouterAttributionHeaders(this.config.openRouterHeaders, {
2023
+ siteUrl: this.config.openRouterSiteUrl,
2024
+ appName: this.config.openRouterAppName
2025
+ });
2026
+ }
1891
2027
  getRateLimiter() {
1892
2028
  if (!this.rateLimiter) {
1893
2029
  this.rateLimiter = new StatelessRateLimiter({
@@ -5644,6 +5780,7 @@ var MediaRouter = class {
5644
5780
  };
5645
5781
 
5646
5782
  // src/ai/OpenRouterMediaProvider.ts
5783
+ init_openrouterAttribution();
5647
5784
  var OPENROUTER_BASE = "https://openrouter.ai/api/v1";
5648
5785
  var DEFAULT_POLL_INTERVAL = 3e4;
5649
5786
  var DEFAULT_TIMEOUT = 6e5;
@@ -5735,9 +5872,14 @@ var OpenRouterMediaProvider = class {
5735
5872
  name = "openrouter";
5736
5873
  supportedModalities = ["image", "audio", "video"];
5737
5874
  baseUrl;
5875
+ attributionHeaders;
5738
5876
  constructor(options = {}) {
5739
5877
  const key = options.apiKey ?? process.env.OPENROUTER_API_KEY ?? "";
5740
5878
  this.baseUrl = options.baseUrl ?? OPENROUTER_BASE;
5879
+ this.attributionHeaders = mergeOpenRouterAttributionHeaders(options.openRouterHeaders, {
5880
+ siteUrl: options.openRouterSiteUrl,
5881
+ appName: options.openRouterAppName
5882
+ });
5741
5883
  if (!key) {
5742
5884
  throw new MediaProviderError("OpenRouter API key required: pass apiKey or set OPENROUTER_API_KEY", {
5743
5885
  provider: "openrouter"
@@ -5897,7 +6039,10 @@ var OpenRouterMediaProvider = class {
5897
6039
  } catch {
5898
6040
  }
5899
6041
  const dlRes = await fetch(videoUrl, {
5900
- headers: downloadHeaders,
6042
+ headers: mergeOpenRouterAttributionHeaders({
6043
+ ...this.attributionHeaders,
6044
+ ...downloadHeaders
6045
+ }),
5901
6046
  signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT),
5902
6047
  redirect: "error"
5903
6048
  });
@@ -6189,10 +6334,11 @@ var OpenRouterMediaProvider = class {
6189
6334
  const key = apiKeyStore.get(this);
6190
6335
  return fetch(url, {
6191
6336
  method: "POST",
6192
- headers: {
6337
+ headers: mergeOpenRouterAttributionHeaders({
6338
+ ...this.attributionHeaders,
6193
6339
  "Content-Type": "application/json",
6194
6340
  Authorization: `Bearer ${key}`
6195
- },
6341
+ }),
6196
6342
  body: JSON.stringify(body),
6197
6343
  signal: AbortSignal.timeout(API_TIMEOUT)
6198
6344
  });
@@ -6201,9 +6347,10 @@ var OpenRouterMediaProvider = class {
6201
6347
  const key = apiKeyStore.get(this);
6202
6348
  return fetch(url, {
6203
6349
  method: "GET",
6204
- headers: {
6350
+ headers: mergeOpenRouterAttributionHeaders({
6351
+ ...this.attributionHeaders,
6205
6352
  Authorization: `Bearer ${key}`
6206
- },
6353
+ }),
6207
6354
  signal: AbortSignal.timeout(API_TIMEOUT)
6208
6355
  });
6209
6356
  }