@probelabs/probe 0.6.0-rc317 → 0.6.0-rc319

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.
@@ -719,14 +719,12 @@ export const searchTool = (options = {}) => {
719
719
  // ── Delegate-level semantic dedup ────────────────────────────
720
720
  // Each delegate is a full flash agent session (minutes, not seconds).
721
721
  // Use LLM to detect semantic duplicates and suggest rewrites.
722
- // Compare against ALL previous delegations (not filtered by path) because
723
- // the parent model often narrows the path while asking the same concept
724
- // (e.g., "dedup" at /src → "deduplicate" at /src/search.js).
725
722
  const delegatePath = searchPath || '';
723
+ const samePathDelegations = previousDelegations.filter(d => d.path === delegatePath);
726
724
 
727
725
  let effectiveQuery = searchQuery;
728
726
 
729
- if (previousDelegations.length > 0) {
727
+ if (samePathDelegations.length > 0) {
730
728
  const dedupProvider = options.searchDelegateProvider || process.env.PROBE_SEARCH_DELEGATE_PROVIDER || options.provider || process.env.FORCE_PROVIDER || null;
731
729
  const dedupModelName = options.searchDelegateModel || process.env.PROBE_SEARCH_DELEGATE_MODEL || options.model || process.env.MODEL_NAME || null;
732
730
  // Lazily create the dedup model (same provider/model as delegate)
@@ -742,8 +740,8 @@ export const searchTool = (options = {}) => {
742
740
 
743
741
  const dedupSpanAttrs = {
744
742
  'dedup.query': searchQuery,
745
- 'dedup.previous_count': String(previousDelegations.length),
746
- 'dedup.previous_queries': previousDelegations.map(d => d.query).join(' | '),
743
+ 'dedup.previous_count': String(samePathDelegations.length),
744
+ 'dedup.previous_queries': samePathDelegations.map(d => d.query).join(' | '),
747
745
  'dedup.provider': dedupProvider || '',
748
746
  'dedup.model': dedupModelName || '',
749
747
  'dedup.model_available': cachedDedupModel ? 'true' : 'false',
@@ -751,7 +749,7 @@ export const searchTool = (options = {}) => {
751
749
 
752
750
  const dedup = options.tracer?.withSpan
753
751
  ? await options.tracer.withSpan('search.delegate.dedup', async () => {
754
- return await checkDelegateDedup(searchQuery, previousDelegations, cachedDedupModel, debug);
752
+ return await checkDelegateDedup(searchQuery, samePathDelegations, cachedDedupModel, debug);
755
753
  }, dedupSpanAttrs, (span, result) => {
756
754
  span.setAttributes({
757
755
  'dedup.action': result.action,
@@ -760,14 +758,14 @@ export const searchTool = (options = {}) => {
760
758
  'dedup.error': result.error || '',
761
759
  });
762
760
  })
763
- : await checkDelegateDedup(searchQuery, previousDelegations, cachedDedupModel, debug);
761
+ : await checkDelegateDedup(searchQuery, samePathDelegations, cachedDedupModel, debug);
764
762
 
765
763
  if (debug) {
766
764
  console.error(`[DEDUP-LLM] Query: "${searchQuery}" → ${dedup.action}: ${dedup.reason}${dedup.rewritten ? ` → "${dedup.rewritten}"` : ''}`);
767
765
  }
768
766
 
769
767
  if (dedup.action === 'block') {
770
- const prevQueries = previousDelegations.map(d => `"${d.query}"`).join(', ');
768
+ const prevQueries = samePathDelegations.map(d => `"${d.query}"`).join(', ');
771
769
  return `DELEGATE BLOCKED: "${searchQuery}" is semantically duplicate of previous delegation(s) [${prevQueries}]. ${dedup.reason}\n\nDo NOT re-delegate the same concept. Use extract() on files already found, or synthesize your answer from existing results.`;
772
770
  }
773
771
 
@@ -84,9 +84,32 @@ export function resolveApiKey(providerName) {
84
84
  }
85
85
  }
86
86
 
87
+ /**
88
+ * Resolve base URL for a provider from environment variables.
89
+ * Mirrors the env-var resolution in ProbeAgent and FallbackManager so that
90
+ * lightweight LLM calls (dedup, etc.) route through the same API gateway.
91
+ * @param {string} providerName
92
+ * @returns {string|undefined}
93
+ */
94
+ export function resolveBaseUrl(providerName) {
95
+ const llmBaseUrl = process.env.LLM_BASE_URL;
96
+ switch (providerName) {
97
+ case 'anthropic':
98
+ return process.env.ANTHROPIC_API_URL || process.env.ANTHROPIC_BASE_URL || llmBaseUrl;
99
+ case 'openai':
100
+ return process.env.OPENAI_API_URL || llmBaseUrl;
101
+ case 'google':
102
+ return process.env.GOOGLE_API_URL || llmBaseUrl;
103
+ case 'bedrock':
104
+ return process.env.AWS_BEDROCK_BASE_URL || llmBaseUrl;
105
+ default:
106
+ return llmBaseUrl;
107
+ }
108
+ }
109
+
87
110
  /**
88
111
  * Create a language model instance from provider name + model name.
89
- * Resolves API keys from environment automatically.
112
+ * Resolves API keys and base URLs from environment automatically.
90
113
  * Returns null on failure (graceful degradation for optional features).
91
114
  * @param {string} providerName - 'anthropic' | 'openai' | 'google' | 'bedrock'
92
115
  * @param {string} modelName - Model identifier (e.g., 'gemini-2.0-flash')
@@ -98,7 +121,8 @@ export async function createLanguageModel(providerName, modelName) {
98
121
  if (!resolvedModel) return null;
99
122
  try {
100
123
  const apiKey = resolveApiKey(providerName);
101
- const provider = createProviderInstance({ provider: providerName, ...(apiKey ? { apiKey } : {}) });
124
+ const baseURL = resolveBaseUrl(providerName);
125
+ const provider = createProviderInstance({ provider: providerName, ...(apiKey ? { apiKey } : {}), ...(baseURL ? { baseURL } : {}) });
102
126
  return provider(resolvedModel);
103
127
  } catch {
104
128
  return null;
@@ -26155,13 +26155,29 @@ function resolveApiKey(providerName) {
26155
26155
  return void 0;
26156
26156
  }
26157
26157
  }
26158
+ function resolveBaseUrl(providerName) {
26159
+ const llmBaseUrl = process.env.LLM_BASE_URL;
26160
+ switch (providerName) {
26161
+ case "anthropic":
26162
+ return process.env.ANTHROPIC_API_URL || process.env.ANTHROPIC_BASE_URL || llmBaseUrl;
26163
+ case "openai":
26164
+ return process.env.OPENAI_API_URL || llmBaseUrl;
26165
+ case "google":
26166
+ return process.env.GOOGLE_API_URL || llmBaseUrl;
26167
+ case "bedrock":
26168
+ return process.env.AWS_BEDROCK_BASE_URL || llmBaseUrl;
26169
+ default:
26170
+ return llmBaseUrl;
26171
+ }
26172
+ }
26158
26173
  async function createLanguageModel(providerName, modelName) {
26159
26174
  if (!providerName) return null;
26160
26175
  const resolvedModel = modelName || DEFAULT_MODELS[providerName];
26161
26176
  if (!resolvedModel) return null;
26162
26177
  try {
26163
26178
  const apiKey = resolveApiKey(providerName);
26164
- const provider = createProviderInstance({ provider: providerName, ...apiKey ? { apiKey } : {} });
26179
+ const baseURL = resolveBaseUrl(providerName);
26180
+ const provider = createProviderInstance({ provider: providerName, ...apiKey ? { apiKey } : {}, ...baseURL ? { baseURL } : {} });
26165
26181
  return provider(resolvedModel);
26166
26182
  } catch {
26167
26183
  return null;
@@ -32551,8 +32567,9 @@ Change your strategy:${scopeHint}
32551
32567
  }
32552
32568
  }
32553
32569
  const delegatePath = searchPath || "";
32570
+ const samePathDelegations = previousDelegations.filter((d) => d.path === delegatePath);
32554
32571
  let effectiveQuery = searchQuery;
32555
- if (previousDelegations.length > 0) {
32572
+ if (samePathDelegations.length > 0) {
32556
32573
  const dedupProvider = options.searchDelegateProvider || process.env.PROBE_SEARCH_DELEGATE_PROVIDER || options.provider || process.env.FORCE_PROVIDER || null;
32557
32574
  const dedupModelName = options.searchDelegateModel || process.env.PROBE_SEARCH_DELEGATE_MODEL || options.model || process.env.MODEL_NAME || null;
32558
32575
  if (cachedDedupModel === void 0) {
@@ -32566,14 +32583,14 @@ Change your strategy:${scopeHint}
32566
32583
  }
32567
32584
  const dedupSpanAttrs = {
32568
32585
  "dedup.query": searchQuery,
32569
- "dedup.previous_count": String(previousDelegations.length),
32570
- "dedup.previous_queries": previousDelegations.map((d) => d.query).join(" | "),
32586
+ "dedup.previous_count": String(samePathDelegations.length),
32587
+ "dedup.previous_queries": samePathDelegations.map((d) => d.query).join(" | "),
32571
32588
  "dedup.provider": dedupProvider || "",
32572
32589
  "dedup.model": dedupModelName || "",
32573
32590
  "dedup.model_available": cachedDedupModel ? "true" : "false"
32574
32591
  };
32575
32592
  const dedup = options.tracer?.withSpan ? await options.tracer.withSpan("search.delegate.dedup", async () => {
32576
- return await checkDelegateDedup(searchQuery, previousDelegations, cachedDedupModel, debug);
32593
+ return await checkDelegateDedup(searchQuery, samePathDelegations, cachedDedupModel, debug);
32577
32594
  }, dedupSpanAttrs, (span, result) => {
32578
32595
  span.setAttributes({
32579
32596
  "dedup.action": result.action,
@@ -32581,12 +32598,12 @@ Change your strategy:${scopeHint}
32581
32598
  "dedup.rewritten": result.rewritten || "",
32582
32599
  "dedup.error": result.error || ""
32583
32600
  });
32584
- }) : await checkDelegateDedup(searchQuery, previousDelegations, cachedDedupModel, debug);
32601
+ }) : await checkDelegateDedup(searchQuery, samePathDelegations, cachedDedupModel, debug);
32585
32602
  if (debug) {
32586
32603
  console.error(`[DEDUP-LLM] Query: "${searchQuery}" \u2192 ${dedup.action}: ${dedup.reason}${dedup.rewritten ? ` \u2192 "${dedup.rewritten}"` : ""}`);
32587
32604
  }
32588
32605
  if (dedup.action === "block") {
32589
- const prevQueries = previousDelegations.map((d) => `"${d.query}"`).join(", ");
32606
+ const prevQueries = samePathDelegations.map((d) => `"${d.query}"`).join(", ");
32590
32607
  return `DELEGATE BLOCKED: "${searchQuery}" is semantically duplicate of previous delegation(s) [${prevQueries}]. ${dedup.reason}
32591
32608
 
32592
32609
  Do NOT re-delegate the same concept. Use extract() on files already found, or synthesize your answer from existing results.`;
package/cjs/index.cjs CHANGED
@@ -28084,13 +28084,29 @@ function resolveApiKey(providerName) {
28084
28084
  return void 0;
28085
28085
  }
28086
28086
  }
28087
+ function resolveBaseUrl(providerName) {
28088
+ const llmBaseUrl = process.env.LLM_BASE_URL;
28089
+ switch (providerName) {
28090
+ case "anthropic":
28091
+ return process.env.ANTHROPIC_API_URL || process.env.ANTHROPIC_BASE_URL || llmBaseUrl;
28092
+ case "openai":
28093
+ return process.env.OPENAI_API_URL || llmBaseUrl;
28094
+ case "google":
28095
+ return process.env.GOOGLE_API_URL || llmBaseUrl;
28096
+ case "bedrock":
28097
+ return process.env.AWS_BEDROCK_BASE_URL || llmBaseUrl;
28098
+ default:
28099
+ return llmBaseUrl;
28100
+ }
28101
+ }
28087
28102
  async function createLanguageModel(providerName, modelName) {
28088
28103
  if (!providerName) return null;
28089
28104
  const resolvedModel = modelName || DEFAULT_MODELS[providerName];
28090
28105
  if (!resolvedModel) return null;
28091
28106
  try {
28092
28107
  const apiKey = resolveApiKey(providerName);
28093
- const provider = createProviderInstance({ provider: providerName, ...apiKey ? { apiKey } : {} });
28108
+ const baseURL = resolveBaseUrl(providerName);
28109
+ const provider = createProviderInstance({ provider: providerName, ...apiKey ? { apiKey } : {}, ...baseURL ? { baseURL } : {} });
28094
28110
  return provider(resolvedModel);
28095
28111
  } catch {
28096
28112
  return null;
@@ -107967,8 +107983,9 @@ Change your strategy:${scopeHint}
107967
107983
  }
107968
107984
  }
107969
107985
  const delegatePath = searchPath || "";
107986
+ const samePathDelegations = previousDelegations.filter((d) => d.path === delegatePath);
107970
107987
  let effectiveQuery = searchQuery;
107971
- if (previousDelegations.length > 0) {
107988
+ if (samePathDelegations.length > 0) {
107972
107989
  const dedupProvider = options.searchDelegateProvider || process.env.PROBE_SEARCH_DELEGATE_PROVIDER || options.provider || process.env.FORCE_PROVIDER || null;
107973
107990
  const dedupModelName = options.searchDelegateModel || process.env.PROBE_SEARCH_DELEGATE_MODEL || options.model || process.env.MODEL_NAME || null;
107974
107991
  if (cachedDedupModel === void 0) {
@@ -107982,14 +107999,14 @@ Change your strategy:${scopeHint}
107982
107999
  }
107983
108000
  const dedupSpanAttrs = {
107984
108001
  "dedup.query": searchQuery,
107985
- "dedup.previous_count": String(previousDelegations.length),
107986
- "dedup.previous_queries": previousDelegations.map((d) => d.query).join(" | "),
108002
+ "dedup.previous_count": String(samePathDelegations.length),
108003
+ "dedup.previous_queries": samePathDelegations.map((d) => d.query).join(" | "),
107987
108004
  "dedup.provider": dedupProvider || "",
107988
108005
  "dedup.model": dedupModelName || "",
107989
108006
  "dedup.model_available": cachedDedupModel ? "true" : "false"
107990
108007
  };
107991
108008
  const dedup = options.tracer?.withSpan ? await options.tracer.withSpan("search.delegate.dedup", async () => {
107992
- return await checkDelegateDedup(searchQuery, previousDelegations, cachedDedupModel, debug);
108009
+ return await checkDelegateDedup(searchQuery, samePathDelegations, cachedDedupModel, debug);
107993
108010
  }, dedupSpanAttrs, (span, result) => {
107994
108011
  span.setAttributes({
107995
108012
  "dedup.action": result.action,
@@ -107997,12 +108014,12 @@ Change your strategy:${scopeHint}
107997
108014
  "dedup.rewritten": result.rewritten || "",
107998
108015
  "dedup.error": result.error || ""
107999
108016
  });
108000
- }) : await checkDelegateDedup(searchQuery, previousDelegations, cachedDedupModel, debug);
108017
+ }) : await checkDelegateDedup(searchQuery, samePathDelegations, cachedDedupModel, debug);
108001
108018
  if (debug) {
108002
108019
  console.error(`[DEDUP-LLM] Query: "${searchQuery}" \u2192 ${dedup.action}: ${dedup.reason}${dedup.rewritten ? ` \u2192 "${dedup.rewritten}"` : ""}`);
108003
108020
  }
108004
108021
  if (dedup.action === "block") {
108005
- const prevQueries = previousDelegations.map((d) => `"${d.query}"`).join(", ");
108022
+ const prevQueries = samePathDelegations.map((d) => `"${d.query}"`).join(", ");
108006
108023
  return `DELEGATE BLOCKED: "${searchQuery}" is semantically duplicate of previous delegation(s) [${prevQueries}]. ${dedup.reason}
108007
108024
 
108008
108025
  Do NOT re-delegate the same concept. Use extract() on files already found, or synthesize your answer from existing results.`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@probelabs/probe",
3
- "version": "0.6.0-rc317",
3
+ "version": "0.6.0-rc319",
4
4
  "description": "Node.js wrapper for the probe code search tool",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
@@ -719,14 +719,12 @@ export const searchTool = (options = {}) => {
719
719
  // ── Delegate-level semantic dedup ────────────────────────────
720
720
  // Each delegate is a full flash agent session (minutes, not seconds).
721
721
  // Use LLM to detect semantic duplicates and suggest rewrites.
722
- // Compare against ALL previous delegations (not filtered by path) because
723
- // the parent model often narrows the path while asking the same concept
724
- // (e.g., "dedup" at /src → "deduplicate" at /src/search.js).
725
722
  const delegatePath = searchPath || '';
723
+ const samePathDelegations = previousDelegations.filter(d => d.path === delegatePath);
726
724
 
727
725
  let effectiveQuery = searchQuery;
728
726
 
729
- if (previousDelegations.length > 0) {
727
+ if (samePathDelegations.length > 0) {
730
728
  const dedupProvider = options.searchDelegateProvider || process.env.PROBE_SEARCH_DELEGATE_PROVIDER || options.provider || process.env.FORCE_PROVIDER || null;
731
729
  const dedupModelName = options.searchDelegateModel || process.env.PROBE_SEARCH_DELEGATE_MODEL || options.model || process.env.MODEL_NAME || null;
732
730
  // Lazily create the dedup model (same provider/model as delegate)
@@ -742,8 +740,8 @@ export const searchTool = (options = {}) => {
742
740
 
743
741
  const dedupSpanAttrs = {
744
742
  'dedup.query': searchQuery,
745
- 'dedup.previous_count': String(previousDelegations.length),
746
- 'dedup.previous_queries': previousDelegations.map(d => d.query).join(' | '),
743
+ 'dedup.previous_count': String(samePathDelegations.length),
744
+ 'dedup.previous_queries': samePathDelegations.map(d => d.query).join(' | '),
747
745
  'dedup.provider': dedupProvider || '',
748
746
  'dedup.model': dedupModelName || '',
749
747
  'dedup.model_available': cachedDedupModel ? 'true' : 'false',
@@ -751,7 +749,7 @@ export const searchTool = (options = {}) => {
751
749
 
752
750
  const dedup = options.tracer?.withSpan
753
751
  ? await options.tracer.withSpan('search.delegate.dedup', async () => {
754
- return await checkDelegateDedup(searchQuery, previousDelegations, cachedDedupModel, debug);
752
+ return await checkDelegateDedup(searchQuery, samePathDelegations, cachedDedupModel, debug);
755
753
  }, dedupSpanAttrs, (span, result) => {
756
754
  span.setAttributes({
757
755
  'dedup.action': result.action,
@@ -760,14 +758,14 @@ export const searchTool = (options = {}) => {
760
758
  'dedup.error': result.error || '',
761
759
  });
762
760
  })
763
- : await checkDelegateDedup(searchQuery, previousDelegations, cachedDedupModel, debug);
761
+ : await checkDelegateDedup(searchQuery, samePathDelegations, cachedDedupModel, debug);
764
762
 
765
763
  if (debug) {
766
764
  console.error(`[DEDUP-LLM] Query: "${searchQuery}" → ${dedup.action}: ${dedup.reason}${dedup.rewritten ? ` → "${dedup.rewritten}"` : ''}`);
767
765
  }
768
766
 
769
767
  if (dedup.action === 'block') {
770
- const prevQueries = previousDelegations.map(d => `"${d.query}"`).join(', ');
768
+ const prevQueries = samePathDelegations.map(d => `"${d.query}"`).join(', ');
771
769
  return `DELEGATE BLOCKED: "${searchQuery}" is semantically duplicate of previous delegation(s) [${prevQueries}]. ${dedup.reason}\n\nDo NOT re-delegate the same concept. Use extract() on files already found, or synthesize your answer from existing results.`;
772
770
  }
773
771
 
@@ -84,9 +84,32 @@ export function resolveApiKey(providerName) {
84
84
  }
85
85
  }
86
86
 
87
+ /**
88
+ * Resolve base URL for a provider from environment variables.
89
+ * Mirrors the env-var resolution in ProbeAgent and FallbackManager so that
90
+ * lightweight LLM calls (dedup, etc.) route through the same API gateway.
91
+ * @param {string} providerName
92
+ * @returns {string|undefined}
93
+ */
94
+ export function resolveBaseUrl(providerName) {
95
+ const llmBaseUrl = process.env.LLM_BASE_URL;
96
+ switch (providerName) {
97
+ case 'anthropic':
98
+ return process.env.ANTHROPIC_API_URL || process.env.ANTHROPIC_BASE_URL || llmBaseUrl;
99
+ case 'openai':
100
+ return process.env.OPENAI_API_URL || llmBaseUrl;
101
+ case 'google':
102
+ return process.env.GOOGLE_API_URL || llmBaseUrl;
103
+ case 'bedrock':
104
+ return process.env.AWS_BEDROCK_BASE_URL || llmBaseUrl;
105
+ default:
106
+ return llmBaseUrl;
107
+ }
108
+ }
109
+
87
110
  /**
88
111
  * Create a language model instance from provider name + model name.
89
- * Resolves API keys from environment automatically.
112
+ * Resolves API keys and base URLs from environment automatically.
90
113
  * Returns null on failure (graceful degradation for optional features).
91
114
  * @param {string} providerName - 'anthropic' | 'openai' | 'google' | 'bedrock'
92
115
  * @param {string} modelName - Model identifier (e.g., 'gemini-2.0-flash')
@@ -98,7 +121,8 @@ export async function createLanguageModel(providerName, modelName) {
98
121
  if (!resolvedModel) return null;
99
122
  try {
100
123
  const apiKey = resolveApiKey(providerName);
101
- const provider = createProviderInstance({ provider: providerName, ...(apiKey ? { apiKey } : {}) });
124
+ const baseURL = resolveBaseUrl(providerName);
125
+ const provider = createProviderInstance({ provider: providerName, ...(apiKey ? { apiKey } : {}), ...(baseURL ? { baseURL } : {}) });
102
126
  return provider(resolvedModel);
103
127
  } catch {
104
128
  return null;