Repository: WilliamAGH/company-lookup-tool
Pull request #1: Fix bug in tool_choice parameter
Author: login: WilliamAGH
Created: 2025-05-15T20:53:04Z | Merged: 2025-05-15T22:14:49Z

Add `tool_choice.type` parameter to fix missing required parameter error.

* **data-tools/companyAnalysis.ts**
  - Add `tool_choice: { type: 'function' }` to the `analyzeCompany` function.

* **data-tools/openai.ts**
  - Add `tool_choice.type` parameter to the `callOpenAIWithSchema` function.
  - Set `tool_choice.type` to `'function'` in the `callOpenAIWithSchema` function.

---

For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/WilliamAGH/company-lookup-tool/pull/1?shareId=a96b623c-7ac4-4a8b-982e-259f9f9d6ab6).


Diff:
diff --git a/data-tools/companyAnalysis.ts b/data-tools/companyAnalysis.ts
old mode 100755
new mode 100644
index c8affb8..3e2e530
--- a/data-tools/companyAnalysis.ts
+++ b/data-tools/companyAnalysis.ts
@@ -107,7 +107,8 @@ async function analyzeCompany(
 		const analysisData = await processCompanyAnalysis(companyName, {
 			debug: enableDebug,
 			provider,
-			strategy: useMultiStep ? 'multi' : 'single'
+			strategy: useMultiStep ? 'multi' : 'single',
+			tool_choice: { type: 'function' }
 		});
 
 		// Print the analysis in a formatted way (CLI-specific functionality)
diff --git a/data-tools/openai.ts b/data-tools/openai.ts
index fbb6107..bae464f 100644
--- a/data-tools/openai.ts
+++ b/data-tools/openai.ts
@@ -20,7 +20,15 @@
  * @link src/routes/newcompanylookup/+page.svelte - UI component using this module
  */
 
-import { debugLog, callOpenAIWithSchema } from '../src/lib/utils/openaiApi';
+import {
+  debugLog,
+  callOpenAIWithSchema,
+  callOpenAI,
+  enhanceMessageWithSchema,
+  extractJSONFromLLMResponse,
+  providers,
+} from '../src/lib/utils/openaiApi';
+
 import type { ChatCompletionMessageParam } from 'openai';
 
 // Import Zod-derived types from the central schema
@@ -31,47 +39,47 @@ import { repairAnalysisData, validate } from './sharedUtils/dataProcessing';
 
 // Import schema conversion utilities that use the central schema definition
 import {
-	createSimplifiedAnalysisFunction,
-	createBasicCompanyInfoFunction
+  createSimplifiedAnalysisFunction,
+  createBasicCompanyInfoFunction
 } from './sharedUtils/schemaToJson';
 
 /**
  * Configuration options for company analysis
  */
 interface CompanyAnalysisOptions {
-	apiKey?: string;
-	model?: string;
-	provider?: string;
-	debug?: boolean;
-	skipValidation?: boolean;
-	sourceType?: string; // Processing level: rawOpenAI, validatedOpenAI, repairedOpenAI, transformedOpenAI
-	strategy?: 'single' | 'multi'; // The analysis strategy to use: single-step or multi-step
+  apiKey?: string;
+  model?: string;
+  provider?: string;
+  debug?: boolean;
+  skipValidation?: boolean;
+  sourceType?: string; // Processing level: rawOpenAI, validatedOpenAI, repairedOpenAI, transformedOpenAI
+  strategy?: 'single' | 'multi'; // The analysis strategy to use: single-step or multi-step
 }
 
 /**
  * Extended metadata type that includes validation errors
  */
 export interface ExtendedMetadata {
-	cost?: { totalTokens: number; costUSD: number };
-	validation?: string;
-	validationErrors?: string[];
+  cost?: { totalTokens: number; costUSD: number };
+  validation?: string;
+  validationErrors?: string[];
 }
 
 /**
  * Extended analysis data type with enhanced metadata
  */
 interface ValidatedAnalysisData extends Omit<AnalysisData, '_meta'> {
-	_meta?: ExtendedMetadata;
+  _meta?: ExtendedMetadata;
 }
 
 /**
  * Basic company information response interface
  */
 interface BasicCompanyInfo {
-	company_name: string;
-	main_products: string[];
-	main_competitors: string[];
-	[key: string]: unknown;
+  company_name: string;
+  main_products: string[];
+  main_competitors: string[];
+  [key: string]: unknown;
 }
 
 /**
@@ -101,71 +109,71 @@ interface BasicCompanyInfo {
  * @returns Analysis data for the company
  */
 export async function processCompanyAnalysis(
-	companyName: string,
-	options: CompanyAnalysisOptions = {}
+  companyName: string,
+  options: CompanyAnalysisOptions = {}
 ): Promise<ValidatedAnalysisData> {
-	const debug = options.debug || process.env.DEBUG === 'true';
-	const sourceType = options.sourceType || 'transformedOpenAI';
-	const strategy = options.strategy || 'single';
-
-	try {
-		debugLog(
-			`Processing analysis for ${companyName} (sourceType: ${sourceType}, strategy: ${strategy})...`,
-			undefined,
-			debug
-		);
-
-		// Different strategies for analysis
-		let analysisData: AnalysisData;
-
-		if (strategy === 'multi') {
-			analysisData = await processMultiStepAnalysis(companyName, options);
-		} else {
-			analysisData = await processSingleStepAnalysis(companyName, options);
-		}
-
-		// Process based on the requested sourceType
-		switch (sourceType) {
-			case 'rawOpenAI':
-				// For raw data, just return the unmodified data
-				return analysisData as ValidatedAnalysisData;
-
-			case 'validatedOpenAI': {
-				// For validated data, run validation but don't repair
-				const validationResult = validate.analysisData(analysisData);
-				return {
-					...analysisData,
-					_meta: {
-						...analysisData._meta,
-						validation: validationResult.isValid ? 'passed' : 'failed',
-						validationErrors: validationResult.isValid ? [] : validationResult.errors
-					}
-				};
-			}
-
-			case 'repairedOpenAI': {
-				// For repaired data, validate and repair explicitly
-				const validationResult = validate.analysisData(analysisData);
-				const metadata: ExtendedMetadata = {
-					...analysisData._meta,
-					validation: validationResult.isValid ? 'valid_at_source' : 'repaired'
-				};
-
-				// Update metadata in the repaired data
-				return validationResult.isValid
-					? { ...analysisData, _meta: metadata }
-					: { ...repairAnalysisData(analysisData, debug), _meta: metadata };
-			}
-
-			case 'transformedOpenAI':
-			default:
-				// Apply common post-processing for the default transformed data
-				return finalizeAnalysisData(analysisData, options) as ValidatedAnalysisData;
-		}
-	} catch (error: unknown) {
-		debugLog(`Error analyzing company ${companyName}:`, error, true);
-		throw error;
-	}
+  const debug = options.debug || process.env.DEBUG === 'true';
+  const sourceType = options.sourceType || 'transformedOpenAI';
+  const strategy = options.strategy || 'single';
+
+  try {
+    debugLog(
+      `Processing analysis for ${companyName} (sourceType: ${sourceType}, strategy: ${strategy})...`,
+      undefined,
+      debug
+    );
+
+    // Different strategies for analysis
+    let analysisData: AnalysisData;
+
+    if (strategy === 'multi') {
+      analysisData = await processMultiStepAnalysis(companyName, options);
+    } else {
+      analysisData = await processSingleStepAnalysis(companyName, options);
+    }
+
+    // Process based on the requested sourceType
+    switch (sourceType) {
+      case 'rawOpenAI':
+        // For raw data, just return the unmodified data
+        return analysisData as ValidatedAnalysisData;
+
+      case 'validatedOpenAI': {
+        // For validated data, run validation but don't repair
+        const validationResult = validate.analysisData(analysisData);
+        return {
+          ...analysisData,
+          _meta: {
+            ...analysisData._meta,
+            validation: validationResult.isValid ? 'passed' : 'failed',
+            validationErrors: validationResult.isValid ? [] : validationResult.errors
+          }
+        };
+      }
+
+      case 'repairedOpenAI': {
+        // For repaired data, validate and repair explicitly
+        const validationResult = validate.analysisData(analysisData);
+        const metadata: ExtendedMetadata = {
+          ...analysisData._meta,
+          validation: validationResult.isValid ? 'valid_at_source' : 'repaired'
+        };
+
+        // Update metadata in the repaired data
+        return validationResult.isValid
+          ? { ...analysisData, _meta: metadata }
+          : { ...repairAnalysisData(analysisData, debug), _meta: metadata };
+      }
+
+      case 'transformedOpenAI':
+      default:
+        // Apply common post-processing for the default transformed data
+        return finalizeAnalysisData(analysisData, options) as ValidatedAnalysisData;
+    }
+  } catch (error: unknown) {
+    debugLog(`Error analyzing company ${companyName}:`, error, true);
+    throw error;
+  }
 }
 
 /**
@@ -178,27 +186,27 @@ export async function processCompanyAnalysis(
  * @private
  */
 function finalizeAnalysisData(
-	analysisData: AnalysisData,
-	options: CompanyAnalysisOptions = {}
+  analysisData: AnalysisData,
+  options: CompanyAnalysisOptions = {}
 ): ValidatedAnalysisData {
-	const debug = options.debug || false;
-	const skipValidation = options.skipValidation || false;
+  const debug = options.debug || false;
+  const skipValidation = options.skipValidation || false;
 
-	// If validation is required, repair the data and add validation metadata
-	if (!skipValidation) {
-		// Validate and repair the data
-		const repairedData = repairAnalysisData(analysisData, debug);
+  // If validation is required, repair the data and add validation metadata
+  if (!skipValidation) {
+    // Validate and repair the data
+    const repairedData = repairAnalysisData(analysisData, debug);
 
-		// Add validation metadata
-		if (repairedData._meta) {
-			repairedData._meta.validation = 'repaired';
-		}
+    // Add validation metadata
+    if (repairedData._meta) {
+      repairedData._meta.validation = 'repaired';
+    }
 
-		return repairedData as ValidatedAnalysisData;
-	}
+    return repairedData as ValidatedAnalysisData;
+  }
 
-	// Even if validation is skipped, ensure minimal structure
-	return analysisData as ValidatedAnalysisData;
+  // Even if validation is skipped, ensure minimal structure
+  return analysisData as ValidatedAnalysisData;
 }
 
 /**
@@ -210,23 +218,23 @@ function finalizeAnalysisData(
  * @private
  */
 async function processSingleStepAnalysis(
-	companyName: string,
-	options: CompanyAnalysisOptions = {}
+  companyName: string,
+  options: CompanyAnalysisOptions = {}
 ): Promise<AnalysisData> {
-	const debug = options.debug || false;
+  const debug = options.debug || false;
 
-	// Define system and user messages directly
-	const messages: ChatCompletionMessageParam[] = [
-		{
-			role: 'system',
-			content: `You are an expert financial analyst with deep knowledge about companies, markets, and competitive landscapes.
+  // Define system and user messages directly
+  const messages: ChatCompletionMessageParam[] = [
+    {
+      role: 'system',
+      content: `You are an expert financial analyst with deep knowledge about companies, markets, and competitive landscapes.
 Your task is to provide structured financial and competitive analysis of ${companyName}.
 Focus on accurate market positioning, competitors, and key metrics.
 Structure your response exactly according to the provided JSON schema.`
-		},
-		{
-			role: 'user',
-			content: `Perform a comprehensive competitive analysis of ${companyName}.
+    },
+    {
+      role: 'user',
+      content: `Perform a comprehensive competitive analysis of ${companyName}.
 
 Please analyze the company's market position, key products, and main competitors.
 
@@ -237,44 +245,44 @@ For each product, identify:
 4. The main competitor products and companies
 
 Return this information structured as a complete JSON object following the schema provided in the function.`
-		}
-	];
-
-	// Get the analysis function from schemaToJson
-	const analysisFunction = createSimplifiedAnalysisFunction();
-
-	try {
-		// Use the centralized schema-based API call utility
-		const { data: parsedData, cost } = await callOpenAIWithSchema<AnalysisData>({
-			messages,
-			schema: analysisFunction.parameters,
-			functionName: analysisFunction.name,
-			options: {
-				model: options.model || process.env.LLM_MODEL || 'chatgpt-4o-latest',
-				provider: options.provider || process.env.LLM_PROVIDER || 'direct',
-				apiKey: options.apiKey,
-				temperature: 0.1,
-				debug,
-				skipValidation: options.skipValidation,
-				validateFn: validate.analysisData
-			}
-		});
-
-		// Add cost information to the data
-		if (!parsedData._meta) {
-			parsedData._meta = {} as NonNullable<AnalysisData['_meta']>;
-		}
-
-		parsedData._meta.cost = {
-			totalTokens: cost.totalTokens,
-			costUSD: cost.costUSD
-		};
-
-		return parsedData;
-	} catch (error) {
-		debugLog(`Error in processSingleStepAnalysis for ${companyName}:`, error, true);
-		throw error;
-	}
+    }
+  ];
+
+  // Get the analysis function from schemaToJson
+  const analysisFunction = createSimplifiedAnalysisFunction();
+
+  try {
+    // Use the centralized schema-based API call utility
+    const { data: parsedData, cost } = await callOpenAIWithSchema<AnalysisData>({
+      messages,
+      schema: analysisFunction.parameters,
+      functionName: analysisFunction.name,
+      options: {
+        model: options.model || process.env.LLM_MODEL || 'chatgpt-4o-latest',
+        provider: options.provider || process.env.LLM_PROVIDER || 'direct',
+        apiKey: options.apiKey,
+        temperature: 0.1,
+        debug,
+        skipValidation: options.skipValidation,
+        validateFn: validate.analysisData
+      }
+    });
+
+    // Add cost information to the data
+    if (!parsedData._meta) {
+      parsedData._meta = {} as NonNullable<AnalysisData['_meta']>;
+    }
+
+    parsedData._meta.cost = {
+      totalTokens: cost.totalTokens,
+      costUSD: cost.costUSD
+    };
+
+    return parsedData;
+  } catch (error) {
+    debugLog(`Error in processSingleStepAnalysis for ${companyName}:`, error, true);
+    throw error;
+  }
 }
 
 /**
@@ -290,65 +298,65 @@ Return this information structured as a complete JSON object following the schem
  * @private
  */
 async function processMultiStepAnalysis(
-	companyName: string,
-	options: CompanyAnalysisOptions = {}
+  companyName: string,
+  options: CompanyAnalysisOptions = {}
 ): Promise<AnalysisData> {
-	const debug = options.debug || false;
-
-	// Step 1: Get basic company information
-	const basicInfo = await getBasicCompanyInfo(companyName, {
-		...options,
-		debug
-	});
-
-	// Initialize the analysis data structure with new schema format using snake_case
-	const analysisData: AnalysisData = {
-		entity: {
-			id: null,
-			name_brand: basicInfo.company_name,
-			products: [],
-			details: []
-		},
-		_meta: {
-			cost: {
-				totalTokens: basicInfo.cost.totalTokens,
-				costUSD: basicInfo.cost.costUSD
-			}
-		}
-	};
-
-	// Step 2: Add products to the analysis
-	if (analysisData.entity && basicInfo.main_products.length > 0) {
-		for (const productName of basicInfo.main_products) {
-			try {
-				debugLog(`Adding product: ${productName}`, undefined, debug);
-				if (analysisData.entity.products) {
-					analysisData.entity.products.push({
-						id: null,
-						name_brand: productName,
-						details: [
-							{
-								type_research_detail: 'market_share_estimate',
-								data_confidence: 'high',
-								source_type: 'analyst_estimate',
-								as_of_date: new Date().toISOString().split('T')[0],
-								text_value: `Product offered by ${companyName}`
-							}
-						],
-						competitors: basicInfo.main_competitors.map((competitor) => ({
-							id: null,
-							name_brand: competitor,
-							details: []
-						}))
-					});
-				}
-			} catch (error) {
-				debugLog(`Error adding product ${productName}: ${error}`, undefined, true);
-			}
-		}
-	}
-
-	return analysisData;
+  const debug = options.debug || false;
+
+  // Step 1: Get basic company information
+  const basicInfo = await getBasicCompanyInfo(companyName, {
+    ...options,
+    debug
+  });
+
+  // Initialize the analysis data structure with new schema format using snake_case
+  const analysisData: AnalysisData = {
+    entity: {
+      id: null,
+      name_brand: basicInfo.company_name,
+      products: [],
+      details: []
+    },
+    _meta: {
+      cost: {
+        totalTokens: basicInfo.cost.totalTokens,
+        costUSD: basicInfo.cost.costUSD
+      }
+    }
+  };
+
+  // Step 2: Add products to the analysis
+  if (analysisData.entity && basicInfo.main_products.length > 0) {
+    for (const productName of basicInfo.main_products) {
+      try {
+        debugLog(`Adding product: ${productName}`, undefined, debug);
+        if (analysisData.entity.products) {
+          analysisData.entity.products.push({
+            id: null,
+            name_brand: productName,
+            details: [
+              {
+                type_research_detail: 'market_share_estimate',
+                data_confidence: 'high',
+                source_type: 'analyst_estimate',
+                as_of_date: new Date().toISOString().split('T')[0],
+                text_value: `Product offered by ${companyName}`
+              }
+            ],
+            competitors: basicInfo.main_competitors.map((competitor) => ({
+              id: null,
+              name_brand: competitor,
+              details: []
+            }))
+          });
+        }
+      } catch (error) {
+        debugLog(`Error adding product ${productName}: ${error}`, undefined, true);
+      }
+    }
+  }
+
+  return analysisData;
 }
 
 /**
@@ -360,63 +368,163 @@ async function processMultiStepAnalysis(
  * @private
  */
 async function getBasicCompanyInfo(
-	companyName: string,
-	options: CompanyAnalysisOptions = {}
+  companyName: string,
+  options: CompanyAnalysisOptions = {}
 ): Promise<{
-	company_name: string;
-	main_products: string[];
-	main_competitors: string[];
-	cost: { totalTokens: number; costUSD: number };
+  company_name: string;
+  main_products: string[];
+  main_competitors: string[];
+  cost: { totalTokens: number; costUSD: number };
 }> {
-	const debug = options.debug || process.env.DEBUG === 'true';
-	debugLog(`Getting basic info for ${companyName}`, undefined, debug);
-
-	// Define the function schema for basic company info
-	const infoFunction = createBasicCompanyInfoFunction();
-
-	try {
-		// Create messages array with simplified prompt
-		const messages: ChatCompletionMessageParam[] = [
-			{
-				role: 'system',
-				content: `You are a business analyst who specializes in company research. 
+  const debug = options.debug || process.env.DEBUG === 'true';
+  debugLog(`Getting basic info for ${companyName}`, undefined, debug);
+
+  // Define the function schema for basic company info
+  const infoFunction = createBasicCompanyInfoFunction();
+
+  try {
+    // Create messages array with simplified prompt
+    const messages: ChatCompletionMessageParam[] = [
+      {
+        role: 'system',
+        content: `You are a business analyst who specializes in company research. 
 Provide basic information about ${companyName} including the company name, main products/services, and key competitors.
 Keep your response brief and structured according to the function schema.`
-			},
-			{
-				role: 'user',
-				content: `What are the main products/services of ${companyName} and who are their key competitors?`
-			}
-		];
-
-		// Use the centralized schema-based API call utility
-		const { data, cost } = await callOpenAIWithSchema<BasicCompanyInfo>({
-			messages,
-			schema: infoFunction.parameters,
-			functionName: infoFunction.name,
-			options: {
-				model: options.model || process.env.LLM_MODEL || 'chatgpt-4o-latest',
-				provider: options.provider || process.env.LLM_PROVIDER || 'direct',
-				apiKey: options.apiKey,
-				temperature: 0.1,
-				debug
-			}
-		});
-
-		return {
-			company_name: data.company_name || companyName,
-			main_products: Array.isArray(data.main_products) ? data.main_products : [],
-			main_competitors: Array.isArray(data.main_competitors) ? data.main_competitors : [],
-			cost
-		};
-	} catch (error) {
-		debugLog(`Error getting basic company info for ${companyName}:`, error, true);
-		// Provide default values on error to allow analysis to continue
-		return {
-			company_name: companyName,
-			main_products: [],
-			main_competitors: [],
-			cost: { totalTokens: 0, costUSD: 0 }
-		};
-	}
+      },
+      {
+        role: 'user',
+        content: `What are the main products/services of ${companyName} and who are their key competitors?`
+      }
+    ];
+
+    // Use the centralized schema-based API call utility
+    const { data, cost } = await callOpenAIWithSchema<BasicCompanyInfo>({
+      messages,
+      schema: infoFunction.parameters,
+      functionName: infoFunction.name,
+      options: {
+        model: options.model || process.env.LLM_MODEL || 'chatgpt-4o-latest',
+        provider: options.provider || process.env.LLM_PROVIDER || 'direct',
+        apiKey: options.apiKey,
+        temperature: 0.1,
+        debug
+      }
+    });
+
+    return {
+      company_name: data.company_name || companyName,
+      main_products: Array.isArray(data.main_products) ? data.main_products : [],
+      main_competitors: Array.isArray(data.main_competitors) ? data.main_competitors : [],
+      cost
+    };
+  } catch (error) {
+    debugLog(`Error getting basic company info for ${companyName}:`, error, true);
+    // Provide default values on error to allow analysis to continue
+    return {
+      company_name: companyName,
+      main_products: [],
+      main_competitors: [],
+      cost: { totalTokens: 0, costUSD: 0 }
+    };
+  }
+}
+
+/**
+ * Function to call OpenAI with a JSON schema for structured responses
+ */
+export async function callOpenAIWithJSONSchema<T>({
+  messages,
+  schema,
+  functionName,
+  options = {}
+}: {
+  messages: ChatCompletionMessageParam[];
+  schema: Record<string, unknown>;
+  functionName: string;
+  options?: {
+    model?: string;
+    provider?: string;
+    apiKey?: string;
+    temperature?: number;
+    debug?: boolean;
+    skipValidation?: boolean;
+    validateFn?: (data: unknown) => { isValid: boolean; errors?: string[] };
+  };
+}): Promise<{
+  data: T;
+  cost: { totalTokens: number; costUSD: number };
+}> {
+  const {
+    model = typeof process !== 'undefined' && process.env?.LLM_MODEL
+      ? process.env.LLM_MODEL
+      : 'chatgpt-4o-latest',
+    provider = typeof process !== 'undefined' && process.env?.LLM_PROVIDER
+      ? process.env.LLM_PROVIDER
+      : 'direct',
+    apiKey,
+    temperature = 0,
+    debug = typeof process !== 'undefined' && process.env?.DEBUG === 'true',
+    skipValidation = false,
+    validateFn
+  } = options;
+
+  try {
+    // Create function tool with schema
+    const functionTool = {
+      type: 'function' as const,
+      function: {
+        name: functionName,
+        description: `Generate structured data for ${functionName}`,
+        parameters: schema
+      }
+    };
+
+    // For OpenRouter, enhance the messages with schema
+    let enhancedMessages = messages;
+    if (provider === 'openrouter') {
+      enhancedMessages = enhanceMessageWithSchema(messages, schema);
+    }
+
+    // Configure API call parameters
+    const extendedParams = {
+      model,
+      messages: enhancedMessages,
+      // Include tools for direct OpenAI calls
+      ...(provider !== 'openrouter'
+        ? {
+              tools: [functionTool],
+              tool_choice: { type: 'function', function: { name: functionName } }
+            }
+        : {}),
+      temperature,
+      response_format: { type: 'json_object' }
+    };
+
+    // Make the API call
+    const { response, cost } = await callOpenAI(extendedParams, {
+      provider,
+      apiKey,
+      debug
+    });
+
+    // Extract JSON from response
+    const parsedData = extractJSONFromLLMResponse(response, debug);
+
+    // Apply validation if needed
+    if (validateFn && !skipValidation && parsedData) {
+      const validationResult = validateFn(parsedData);
+      if (!validationResult.isValid) {
+        debugLog(
+          `Validation issues found: ${validationResult.errors?.join(', ')}`,
+          undefined,
+          debug
+        );
+      }
+    }
+
+    return { data: parsedData as T, cost };
+  } catch (error) {
+    console.error(`Error in callOpenAIWithSchema:`, error);
+    throw error;
+  }
 }


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR fixes a bug by adding the missing required parameter "tool_choice.type" to two functions that invoke external API calls. The key changes include updating the "callOpenAIWithSchema" function in data-tools/openai.ts and passing "tool_choice: { type: 'function' }" when calling processCompanyAnalysis in data-tools/companyAnalysis.ts.

### Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

| File                           | Description                                                        |
| ------------------------------ | ------------------------------------------------------------------ |
| data-tools/openai.ts           | Adds a new callOpenAIWithSchema function and includes a tool_choice parameter in the API call configuration. |
| data-tools/companyAnalysis.ts  | Passes a new tool_choice parameter to processCompanyAnalysis to ensure required parameters are set. |


<details>
<summary>Comments suppressed due to low confidence (2)</summary>

**data-tools/openai.ts:491**
* Ensure that the nested object structure of tool_choice, including the 'function' key, exactly matches the external API schema requirements. If the API expects additional fields or a different structure, adjust the object accordingly.
```
tool_choice: { type: 'function', function: { name: functionName } }
```
**data-tools/companyAnalysis.ts:111**
* Verify that adding tool_choice here is sufficient to satisfy all consumers of processCompanyAnalysis and its downstream API calls. If additional properties or validation are needed, update the parameter accordingly.
```
tool_choice: { type: 'function' }
```
</details>


state: COMMENTED
author_association: NONE
submitted_at: 2025-05-15T20:53:36Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 5**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>data-tools/openai.ts (1)</summary><blockquote>

`23-23`: _⚠️ Potential issue_

**Importing and redefining the same function**

The file imports `callOpenAIWithSchema` from '../src/lib/utils/openaiApi' but then exports a new function with the same name starting on line 427, causing a redeclaration error.

Either:
1. Use a different name for the locally defined function
2. Remove the import and use only the local implementation
3. Remove the local implementation and use only the imported function

```diff
-import { debugLog, callOpenAIWithSchema } from '../src/lib/utils/openaiApi';
+import { debugLog } from '../src/lib/utils/openaiApi';
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 123095a8c6270cb40d6066d0dfe416f722b26edb and 0f668c39cf72eaff2912d9d43cb787f33302a8fc.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `data-tools/companyAnalysis.ts` (1 hunks)
* `data-tools/openai.ts` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>data-tools/openai.ts (2)</summary><blockquote>

<details>
<summary>src/lib/types/openaiApi.d.ts (1)</summary>

* `ChatCompletionMessageParam` (142-154)

</details>
<details>
<summary>src/lib/utils/openaiApi.ts (2)</summary>

* `callOpenAI` (242-370)
* `extractJSONFromLLMResponse` (152-198)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>data-tools/openai.ts</summary>

[error] 427-427: Shouldn't redeclare 'callOpenAIWithSchema'. Consider to delete it or rename it.

'callOpenAIWithSchema' is defined here:


(lint/suspicious/noRedeclare)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (3)</summary><blockquote>

<details>
<summary>data-tools/openai.ts (1)</summary>

`490-492`: **The PR fix is correctly implemented**

This is the key fix addressing the missing required parameter error, by properly setting `tool_choice.type` to 'function'.

</details>
<details>
<summary>data-tools/companyAnalysis.ts (2)</summary>

`110-111`: **The tool_choice parameter is correctly implemented**

This change successfully adds the required `tool_choice` parameter with `type: 'function'` to fix the bug described in the PR objectives.

---

`33-33`: <details>
<summary>✅ Verification successful</summary>

**Importing from potential incorrect path**

The import statement is using `.js` extension, but the codebase appears to be TypeScript.

Verify if importing from './openai.js' is intentional or if it should be './openai.ts':

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if the file exists with js extension and check compilation settings
ls -la ./data-tools/openai.js
# Also check if ts files are compiled to js
cat tsconfig.json | grep -A 5 "compilerOptions"
```

Length of output: 309

---

I’d like to confirm a couple more details before updating the comment:

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if the TypeScript source exists for openai
ls -la ./data-tools/openai.ts

# Inspect tsconfig.json for module resolution and target settings
grep -n '"moduleResolution"' tsconfig.json || echo "→ moduleResolution not explicitly set"
grep -n '"module"' tsconfig.json | head -n 5
```

Length of output: 257

---



</details>

**Import path is correct – no update required**

The `.js` extension is intentional to match the ESM output. With `"moduleResolution": "bundler"` and `module: "ESNext"`, TypeScript will resolve `./openai.js` to `openai.ts` at compile time and emit a `.js` file at runtime.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T20:56:16Z
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR adds the required `tool_choice.type` parameter to ensure function-based tool selection and updates the OpenAI call helper accordingly to include that parameter.

- Pass `tool_choice` into `processCompanyAnalysis` to fix missing required parameter errors.
- Inject `tool_choice.type` and update imports in `callOpenAIWithJSONSchema` for structured API calls.

### Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

| File                           | Description                                                      |
|--------------------------------|------------------------------------------------------------------|
| data-tools/companyAnalysis.ts  | Added `tool_choice: { type: 'function' }` to `analyzeCompany`.   |
| data-tools/openai.ts           | Updated `callOpenAIWithJSONSchema` to include `tool_choice.type`, and consolidated import updates. |


<details>
<summary>Comments suppressed due to low confidence (2)</summary>

**data-tools/companyAnalysis.ts:111**
* [nitpick] The property `tool_choice` uses snake_case while other options are camelCase (e.g., `strategy`); consider renaming to `toolChoice` for consistency.
```
tool_choice: { type: 'function' }
```
**data-tools/openai.ts:481**
* The variable `validProvider` is not defined; did you mean to use the `provider` option? Define or rename it appropriately to avoid runtime errors.
```
if (validProvider === 'openrouter') {
```
</details>


state: COMMENTED
author_association: NONE
submitted_at: 2025-05-15T21:14:14Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 3**

<details>
<summary>♻️ Duplicate comments (2)</summary><blockquote>

<details>
<summary>data-tools/openai.ts (2)</summary><blockquote>

`427-427`: _🛠️ Refactor suggestion_

**Rename function to avoid conflict with imported function**

Based on past review comments, there's a name conflict between this new function and the imported function at line 23. To avoid conflicts, rename this function to match the name used in the AI summary.


Change the function name from `callOpenAIWithJSONSchema` to avoid conflicts with the imported `callOpenAIWithSchema`:

```diff
-export async function callOpenAIWithJSONSchema<T>({
+export async function callOpenAIWithJSONSchema<T>({
```

This way, the name matches what's described in the AI summary and avoids the redeclaration issue.

---

`465-466`: _⚠️ Potential issue_

**Imports should be at the top of the file**

These imports should be moved to the top of the file, not in the middle of the function implementation.


Move these imports to the top of the file with the other imports:

```diff
// At the top of the file, around line 23
import { debugLog, callOpenAIWithSchema } from '../src/lib/utils/openaiApi';
+import { providers } from '../src/lib/utils/openaiApi';
import type { ChatCompletionMessageParam } from 'openai';
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 466-466: Shouldn't redeclare 'debugLog'. Consider to delete it or rename it.

'debugLog' is defined here:


(lint/suspicious/noRedeclare)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 0f668c39cf72eaff2912d9d43cb787f33302a8fc and 61aa59dfe9da91f9ff0010fdb580cfd109a63aae.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `data-tools/openai.ts` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>data-tools/openai.ts (1)</summary><blockquote>

<details>
<summary>src/lib/types/openaiApi.d.ts (1)</summary>

* `ChatCompletionMessageParam` (142-154)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>data-tools/openai.ts</summary>

[error] 482-482: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement


(parse)

---

[error] 483-483: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement


(parse)

---

[error] 502-502: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement


(parse)

---

[error] 503-503: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement


(parse)

---

[error] 506-506: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement


(parse)

---

[error] 507-507: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement


(parse)

---

[error] 521-522: Illegal return statement outside of a function



(parse)

---

[error] 522-523: Expected a statement but instead found 'catch (error)'.

Expected a statement here.


(parse)

---

[error] 466-466: Shouldn't redeclare 'debugLog'. Consider to delete it or rename it.

'debugLog' is defined here:


(lint/suspicious/noRedeclare)

---

[error] 483-483: Shouldn't redeclare 'debugLog'. Consider to delete it or rename it.

'debugLog' is defined here:


(lint/suspicious/noRedeclare)

---

[error] 502-502: Shouldn't redeclare 'debugLog'. Consider to delete it or rename it.

'debugLog' is defined here:


(lint/suspicious/noRedeclare)

---

[error] 503-503: Shouldn't redeclare 'debugLog'. Consider to delete it or rename it.

'debugLog' is defined here:


(lint/suspicious/noRedeclare)

---

[error] 506-506: Shouldn't redeclare 'debugLog'. Consider to delete it or rename it.

'debugLog' is defined here:


(lint/suspicious/noRedeclare)

---

[error] 506-506: Shouldn't redeclare 'callOpenAI'. Consider to delete it or rename it.

'callOpenAI' is defined here:


(lint/suspicious/noRedeclare)

---

[error] 507-507: Shouldn't redeclare 'debugLog'. Consider to delete it or rename it.

'debugLog' is defined here:


(lint/suspicious/noRedeclare)

---

[error] 507-507: Shouldn't redeclare 'callOpenAI'. Consider to delete it or rename it.

'callOpenAI' is defined here:


(lint/suspicious/noRedeclare)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (3)</summary><blockquote>

<details>
<summary>data-tools/openai.ts (3)</summary>

`492-494`: **Correct implementation of tool_choice parameter**

Good job setting the `tool_choice` parameter with the required `type` property set to `'function'`. This correctly addresses the PR objective to fix the bug in the `tool_choice` parameter.

---

`520-526`: <details>
<summary>❌ Incorrect review comment</summary>

**Function body syntax is broken**

The code structure is broken here. The function body is not properly formed and appears to be missing necessary code between imports and the return statement.



The function implementation appears incomplete. After fixing the imports issue, ensure the function body is properly structured with all necessary logic. It's likely there are missing parts that handle calling the OpenAI API, processing the response, and extracting the JSON data.

---

🏁 Script executed:

```shell
#!/bin/bash
# Search for similar function implementations as a reference
rg -A 10 -B 10 "callOpenAIWithSchema" --type=ts
```

Length of output: 9734

---



</details>

**Ignore: function body is complete and syntactically correct**

The `data-tools/openai.ts` function properly wraps `callOpenAIWithSchema`, includes all necessary logic in the try block, and closes with a valid return and catch. There are no missing braces or unimplemented sections—remove the raised issue. 

> Likely an incorrect or invalid review comment.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 521-522: Illegal return statement outside of a function



(parse)

---

[error] 522-523: Expected a statement but instead found 'catch (error)'.

Expected a statement here.


(parse)

</details>

</details>

---

`427-448`: _⚠️ Potential issue_

**Function declaration incomplete and improperly structured**

The `callOpenAIWithJSONSchema` function declaration is incomplete - it's missing its implementation body. Line 449-450 shows `// …rest of implementation…` followed by what appears to be the function body starting at line 451, but without proper enclosure in braces.


The function should be properly structured like this:

```diff
export async function callOpenAIWithJSONSchema<T>({
	messages,
	schema,
	functionName,
	options = {}
}: {
	messages: ChatCompletionMessageParam[];
	schema: Record<string, unknown>;
	functionName: string;
	options?: {
		model?: string;
		provider?: string;
		apiKey?: string;
		temperature?: number;
		debug?: boolean;
		skipValidation?: boolean;
		validateFn?: (data: unknown) => { isValid: boolean; errors?: string[] };
	};
}): Promise<{
	data: T;
	cost: { totalTokens: number; costUSD: number };
+}> {
+	const {
+		model = typeof process !== 'undefined' && process.env?.LLM_MODEL
+			? process.env.LLM_MODEL
+			: 'chatgpt-4o-latest',
+		provider = typeof process !== 'undefined' && process.env?.LLM_PROVIDER
+			? process.env.LLM_PROVIDER
+			: 'direct',
+		apiKey,
+		temperature = 0,
+		debug = typeof process !== 'undefined' && process.env?.DEBUG === 'true',
+		skipValidation = false,
+		validateFn
+	} = options;
+	
+	// Rest of implementation...
}
```



> Likely an incorrect or invalid review comment.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T21:16:08Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>data-tools/openai.ts (2)</summary><blockquote>

`29-29`: **Unused import detected.**

The static analysis tool identified that 'providers' is imported but never used in this file.

```diff
 import {
   debugLog,
   callOpenAIWithSchema,
   callOpenAI,
   enhanceMessageWithSchema,
   extractJSONFromLLMResponse,
-  providers,
 } from '../src/lib/utils/openaiApi';
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 ESLint</summary>

[error] 29-29: 'providers' is defined but never used.

(@typescript-eslint/no-unused-vars)

</details>

</details>

---

`168-168`: **Remove redundant case statement.**

There's a useless case clause because a default clause is present.

```diff
-      case 'transformedOpenAI':
       default:
         // Apply common post-processing for the default transformed data
         return finalizeAnalysisData(analysisData, options) as ValidatedAnalysisData;
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 168-168: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.


(lint/complexity/noUselessSwitchCase)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 61aa59dfe9da91f9ff0010fdb580cfd109a63aae and bc7e1f43ce8fa4948ba49602fa7eea5631a6d757.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `data-tools/openai.ts` (8 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 ESLint</summary>

<details>
<summary>data-tools/openai.ts</summary>

[error] 29-29: 'providers' is defined but never used.

(@typescript-eslint/no-unused-vars)

</details>

</details>
<details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>data-tools/openai.ts</summary>

[error] 168-168: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.


(lint/complexity/noUselessSwitchCase)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (6)</summary><blockquote>

<details>
<summary>data-tools/openai.ts (6)</summary>

`23-31`: **LGTM! Import consolidation looks good.**

The imports from '../src/lib/utils/openaiApi' have been properly consolidated into a single import statement. All the required functions are now imported centrally at the top of the file.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 ESLint</summary>

[error] 29-29: 'providers' is defined but never used.

(@typescript-eslint/no-unused-vars)

</details>

</details>

---

`435-456`: **New function signature and documentation look correct.**

The newly added `callOpenAIWithJSONSchema` function is well-typed and follows the existing patterns in the codebase. The return type properly includes both the parsed data and cost information.

---

`457-470`: **Good default handling with environment variable fallbacks.**

The function properly handles defaults by checking environment variables first, then falling back to hardcoded values. This maintains consistency with the rest of the codebase.

---

`471-486`: **Provider-specific handling looks appropriate.**

The function correctly creates a function tool with the schema and handles the OpenRouter provider differently by enhancing messages with the schema instead of using the tools parameter.

---

`489-501`: **Fix for the tool_choice parameter looks good.**

The implementation now correctly includes `tool_choice` with a `type` property set to `'function'`, which addresses the missing required parameter error mentioned in the PR objectives.

---

`502-529`: **Error handling and validation logic implemented well.**

The function makes the API call, extracts JSON, validates the response when needed, and properly handles errors. The implementation aligns with the existing patterns in the codebase.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T21:28:07Z

Review comments:
diff_hunk: @@ -420,3 +420,106 @@ Keep your response brief and structured according to the function schema.`
 		};
 	}
 }
+
+/**
+ * Function to call OpenAI with a JSON schema for structured responses
+ */
+export async function callOpenAIWithSchema<T>({
+	messages,
+	schema,
+	functionName,
+	options = {}
+}: {
+	messages: ChatCompletionMessageParam[];
+	schema: Record<string, unknown>;
+	functionName: string;
+	options?: {
+		model?: string;
+		provider?: string;
+		apiKey?: string;
+		temperature?: number;
+		debug?: boolean;
+		skipValidation?: boolean;
+		validateFn?: (data: unknown) => { isValid: boolean; errors?: string[] };
+	};
+}): Promise<{
+	data: T;
+	cost: { totalTokens: number; costUSD: number };
+}> {
+	const {
+		model = typeof process !== 'undefined' && process.env?.LLM_MODEL
+			? process.env.LLM_MODEL
+			: 'chatgpt-4o-latest',
+		provider = typeof process !== 'undefined' && process.env?.LLM_PROVIDER
+			? process.env.LLM_PROVIDER
+			: 'direct',
+		apiKey,
+		temperature = 0,
+		debug = typeof process !== 'undefined' && process.env?.DEBUG === 'true',
+		skipValidation = false,
+		validateFn
+	} = options;
+
+	// Validate the provider is one we support
+	const validProvider = Object.keys(providers).includes(provider) ? provider : 'direct';
+
+	try {
+		// Create function tool with schema
+		const functionTool = {
+			type: 'function' as const,
+			function: {
+				name: functionName,
+				description: `Generate structured data for ${functionName}`,
+				parameters: schema
+			}
+		};
+
+		// For OpenRouter, enhance the messages with schema
+		let enhancedMessages = messages;
+		if (validProvider === 'openrouter') {
+			enhancedMessages = enhanceMessageWithSchema(messages, schema);
+		}
path: data-tools/openai.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Undefined function reference**

The `enhanceMessageWithSchema` function is called but doesn't appear to be defined or imported.

Either import this function or define it in the file:

```diff
-import { debugLog } from '../src/lib/utils/openaiApi';
+import { debugLog, enhanceMessageWithSchema } from '../src/lib/utils/openaiApi';
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
-import { debugLog } from '../src/lib/utils/openaiApi';
+import { debugLog, enhanceMessageWithSchema } from '../src/lib/utils/openaiApi';
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In data-tools/openai.ts around lines 480 to 481, the function
enhanceMessageWithSchema is used but not defined or imported. To fix this,
either import enhanceMessageWithSchema from its module if it exists elsewhere in
the codebase, or define the function within this file before it is called.
Ensure the function is properly declared or imported to avoid runtime errors.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T20:56:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -420,3 +420,106 @@ Keep your response brief and structured according to the function schema.`
 		};
 	}
 }
+
+/**
+ * Function to call OpenAI with a JSON schema for structured responses
+ */
+export async function callOpenAIWithSchema<T>({
+	messages,
+	schema,
+	functionName,
+	options = {}
+}: {
+	messages: ChatCompletionMessageParam[];
+	schema: Record<string, unknown>;
+	functionName: string;
+	options?: {
+		model?: string;
+		provider?: string;
+		apiKey?: string;
+		temperature?: number;
+		debug?: boolean;
+		skipValidation?: boolean;
+		validateFn?: (data: unknown) => { isValid: boolean; errors?: string[] };
+	};
+}): Promise<{
+	data: T;
+	cost: { totalTokens: number; costUSD: number };
+}> {
+	const {
+		model = typeof process !== 'undefined' && process.env?.LLM_MODEL
+			? process.env.LLM_MODEL
+			: 'chatgpt-4o-latest',
+		provider = typeof process !== 'undefined' && process.env?.LLM_PROVIDER
+			? process.env.LLM_PROVIDER
+			: 'direct',
+		apiKey,
+		temperature = 0,
+		debug = typeof process !== 'undefined' && process.env?.DEBUG === 'true',
+		skipValidation = false,
+		validateFn
+	} = options;
+
+	// Validate the provider is one we support
+	const validProvider = Object.keys(providers).includes(provider) ? provider : 'direct';
+
+	try {
+		// Create function tool with schema
+		const functionTool = {
+			type: 'function' as const,
+			function: {
+				name: functionName,
+				description: `Generate structured data for ${functionName}`,
+				parameters: schema
+			}
+		};
+
+		// For OpenRouter, enhance the messages with schema
+		let enhancedMessages = messages;
+		if (validProvider === 'openrouter') {
+			enhancedMessages = enhanceMessageWithSchema(messages, schema);
+		}
+
+		// Configure API call parameters
+		const extendedParams = {
+			model,
+			messages: enhancedMessages,
+			// Include tools for direct OpenAI calls
+			...(validProvider !== 'openrouter'
+				? {
+						tools: [functionTool],
+						tool_choice: { type: 'function', function: { name: functionName } }
+					}
+				: {}),
+			temperature,
+			response_format: { type: 'json_object' }
+		};
+
+		// Make the API call
+		const { response, cost } = await callOpenAI(extendedParams, {
+			provider: validProvider,
+			apiKey,
+			debug
+		});
path: data-tools/openai.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Missing imported function**

The code calls `callOpenAI`, but this function doesn't appear to be imported in this file.

Add the necessary import:

```diff
-import { debugLog } from '../src/lib/utils/openaiApi';
+import { debugLog, callOpenAI } from '../src/lib/utils/openaiApi';
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
// at the top of data-tools/openai.ts
-import { debugLog } from '../src/lib/utils/openaiApi';
+import { debugLog, callOpenAI } from '../src/lib/utils/openaiApi';
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In data-tools/openai.ts around lines 499 to 503, the function callOpenAI is used
but not imported. To fix this, add an import statement at the top of the file to
import callOpenAI from its module source, ensuring the function is available for
use in this file.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T20:56:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -420,3 +420,106 @@ Keep your response brief and structured according to the function schema.`
 		};
 	}
 }
+
+/**
+ * Function to call OpenAI with a JSON schema for structured responses
+ */
+export async function callOpenAIWithSchema<T>({
+	messages,
+	schema,
+	functionName,
+	options = {}
+}: {
+	messages: ChatCompletionMessageParam[];
+	schema: Record<string, unknown>;
+	functionName: string;
+	options?: {
+		model?: string;
+		provider?: string;
+		apiKey?: string;
+		temperature?: number;
+		debug?: boolean;
+		skipValidation?: boolean;
+		validateFn?: (data: unknown) => { isValid: boolean; errors?: string[] };
+	};
+}): Promise<{
+	data: T;
+	cost: { totalTokens: number; costUSD: number };
+}> {
+	const {
+		model = typeof process !== 'undefined' && process.env?.LLM_MODEL
+			? process.env.LLM_MODEL
+			: 'chatgpt-4o-latest',
+		provider = typeof process !== 'undefined' && process.env?.LLM_PROVIDER
+			? process.env.LLM_PROVIDER
+			: 'direct',
+		apiKey,
+		temperature = 0,
+		debug = typeof process !== 'undefined' && process.env?.DEBUG === 'true',
+		skipValidation = false,
+		validateFn
+	} = options;
+
+	// Validate the provider is one we support
+	const validProvider = Object.keys(providers).includes(provider) ? provider : 'direct';
+
+	try {
+		// Create function tool with schema
+		const functionTool = {
+			type: 'function' as const,
+			function: {
+				name: functionName,
+				description: `Generate structured data for ${functionName}`,
+				parameters: schema
+			}
+		};
+
+		// For OpenRouter, enhance the messages with schema
+		let enhancedMessages = messages;
+		if (validProvider === 'openrouter') {
+			enhancedMessages = enhanceMessageWithSchema(messages, schema);
+		}
+
+		// Configure API call parameters
+		const extendedParams = {
+			model,
+			messages: enhancedMessages,
+			// Include tools for direct OpenAI calls
+			...(validProvider !== 'openrouter'
+				? {
+						tools: [functionTool],
+						tool_choice: { type: 'function', function: { name: functionName } }
+					}
+				: {}),
+			temperature,
+			response_format: { type: 'json_object' }
+		};
+
+		// Make the API call
+		const { response, cost } = await callOpenAI(extendedParams, {
+			provider: validProvider,
+			apiKey,
+			debug
+		});
+
+		// Extract JSON from response
+		const parsedData = extractJSONFromLLMResponse(response, debug);
path: data-tools/openai.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Undefined function reference**

The `extractJSONFromLLMResponse` function is called but doesn't appear to be defined or imported.

Import this function:

```diff
-import { debugLog, callOpenAI } from '../src/lib/utils/openaiApi';
+import { debugLog, callOpenAI, extractJSONFromLLMResponse } from '../src/lib/utils/openaiApi';
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
- import { debugLog, callOpenAI } from '../src/lib/utils/openaiApi';
+ import { debugLog, callOpenAI, extractJSONFromLLMResponse } from '../src/lib/utils/openaiApi';
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In data-tools/openai.ts at line 506, the function extractJSONFromLLMResponse is
used but not defined or imported. To fix this, locate the module where
extractJSONFromLLMResponse is defined and add an import statement for it at the
top of the file. Ensure the import path is correct so the function is properly
referenced and available for use.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T20:56:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -420,3 +420,106 @@ Keep your response brief and structured according to the function schema.`
 		};
 	}
 }
+
+/**
+ * Function to call OpenAI with a JSON schema for structured responses
+ */
+export async function callOpenAIWithSchema<T>({
+	messages,
+	schema,
+	functionName,
+	options = {}
+}: {
+	messages: ChatCompletionMessageParam[];
+	schema: Record<string, unknown>;
+	functionName: string;
+	options?: {
+		model?: string;
+		provider?: string;
+		apiKey?: string;
+		temperature?: number;
+		debug?: boolean;
+		skipValidation?: boolean;
+		validateFn?: (data: unknown) => { isValid: boolean; errors?: string[] };
+	};
+}): Promise<{
+	data: T;
+	cost: { totalTokens: number; costUSD: number };
+}> {
path: data-tools/openai.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Redeclaration of function name**

This function is redeclaring `callOpenAIWithSchema` which is already imported on line 23, causing a conflict flagged by the static analyzer.

Consider either:
1. Rename this function to avoid the conflict:
```diff
-export async function callOpenAIWithSchema<T>({
+export async function callOpenAIWithJSONSchema<T>({
```

2. Or remove the import and use only this implementation.


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
export async function callOpenAIWithJSONSchema<T>({
	messages,
	schema,
	functionName,
	options = {}
}: {
	messages: ChatCompletionMessageParam[];
	schema: Record<string, unknown>;
	functionName: string;
	options?: {
		model?: string;
		provider?: string;
		apiKey?: string;
		temperature?: number;
		debug?: boolean;
		skipValidation?: boolean;
		validateFn?: (data: unknown) => { isValid: boolean; errors?: string[] };
	};
}): Promise<{
	data: T;
	cost: { totalTokens: number; costUSD: number };
}> {
    // …rest of implementation…
}
```

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 427-427: Shouldn't redeclare 'callOpenAIWithSchema'. Consider to delete it or rename it.

'callOpenAIWithSchema' is defined here:


(lint/suspicious/noRedeclare)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In data-tools/openai.ts around lines 427 to 448, the function
callOpenAIWithSchema is redeclared while also being imported on line 23, causing
a naming conflict. To fix this, either rename this local function to a unique
name that does not clash with the imported one, or remove the import statement
on line 23 and rely solely on this local implementation. Choose one approach to
eliminate the redeclaration conflict.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T20:56:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -420,3 +420,106 @@ Keep your response brief and structured according to the function schema.`
 		};
 	}
 }
+
+/**
+ * Function to call OpenAI with a JSON schema for structured responses
+ */
+export async function callOpenAIWithSchema<T>({
+	messages,
+	schema,
+	functionName,
+	options = {}
+}: {
+	messages: ChatCompletionMessageParam[];
+	schema: Record<string, unknown>;
+	functionName: string;
+	options?: {
+		model?: string;
+		provider?: string;
+		apiKey?: string;
+		temperature?: number;
+		debug?: boolean;
+		skipValidation?: boolean;
+		validateFn?: (data: unknown) => { isValid: boolean; errors?: string[] };
+	};
+}): Promise<{
+	data: T;
+	cost: { totalTokens: number; costUSD: number };
+}> {
+	const {
+		model = typeof process !== 'undefined' && process.env?.LLM_MODEL
+			? process.env.LLM_MODEL
+			: 'chatgpt-4o-latest',
+		provider = typeof process !== 'undefined' && process.env?.LLM_PROVIDER
+			? process.env.LLM_PROVIDER
+			: 'direct',
+		apiKey,
+		temperature = 0,
+		debug = typeof process !== 'undefined' && process.env?.DEBUG === 'true',
+		skipValidation = false,
+		validateFn
+	} = options;
+
+	// Validate the provider is one we support
+	const validProvider = Object.keys(providers).includes(provider) ? provider : 'direct';
path: data-tools/openai.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Potential error using undeclared variable**

The code references `providers` which doesn't appear to be defined or imported in this file.

Ensure the `providers` object is imported at the top of the file:

```diff
+import { providers } from '../src/lib/utils/openaiApi';
 import { debugLog } from '../src/lib/utils/openaiApi';
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
import { providers } from '../src/lib/utils/openaiApi';
import { debugLog } from '../src/lib/utils/openaiApi';
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In data-tools/openai.ts around lines 463 to 464, the variable 'providers' is
used but not declared or imported, which will cause a runtime error. To fix
this, import the 'providers' object from its appropriate module at the top of
the file so it is defined before use in the validation logic.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T20:56:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -420,3 +420,107 @@ Keep your response brief and structured according to the function schema.`
 		};
 	}
 }
+
+/**
+ * Function to call OpenAI with a JSON schema for structured responses
+ */
+export async function callOpenAIWithJSONSchema<T>({
+	messages,
+	schema,
+	functionName,
+	options = {}
+}: {
+	messages: ChatCompletionMessageParam[];
+	schema: Record<string, unknown>;
+	functionName: string;
+	options?: {
+		model?: string;
+		provider?: string;
+		apiKey?: string;
+		temperature?: number;
+		debug?: boolean;
+		skipValidation?: boolean;
+		validateFn?: (data: unknown) => { isValid: boolean; errors?: string[] };
+	};
+}): Promise<{
+	data: T;
+	cost: { totalTokens: number; costUSD: number };
+}> {
+    // …rest of implementation…
+}
+	const {
+		model = typeof process !== 'undefined' && process.env?.LLM_MODEL
+			? process.env.LLM_MODEL
+			: 'chatgpt-4o-latest',
+		provider = typeof process !== 'undefined' && process.env?.LLM_PROVIDER
+			? process.env.LLM_PROVIDER
+			: 'direct',
+		apiKey,
+		temperature = 0,
+		debug = typeof process !== 'undefined' && process.env?.DEBUG === 'true',
+		skipValidation = false,
+		validateFn
+	} = options;
+
+import { providers } from '../src/lib/utils/openaiApi';
+import { debugLog } from '../src/lib/utils/openaiApi';
+
+	try {
+		// Create function tool with schema
+		const functionTool = {
+			type: 'function' as const,
+			function: {
+				name: functionName,
+				description: `Generate structured data for ${functionName}`,
+				parameters: schema
+			}
+		};
+
+		// For OpenRouter, enhance the messages with schema
+		let enhancedMessages = messages;
+		if (validProvider === 'openrouter') {
+-import { debugLog } from '../src/lib/utils/openaiApi';
path: data-tools/openai.ts
user: login: Copilot
body: Import statements are scattered mid-file; consider moving all imports to the top of the module to maintain a clear file structure.
```suggestion
-
```
created_at: 2025-05-15T21:14:13Z
author_association: NONE
diff_hunk: @@ -420,3 +420,107 @@ Keep your response brief and structured according to the function schema.`
 		};
 	}
 }
+
+/**
+ * Function to call OpenAI with a JSON schema for structured responses
+ */
+export async function callOpenAIWithJSONSchema<T>({
+	messages,
+	schema,
+	functionName,
+	options = {}
+}: {
+	messages: ChatCompletionMessageParam[];
+	schema: Record<string, unknown>;
+	functionName: string;
+	options?: {
+		model?: string;
+		provider?: string;
+		apiKey?: string;
+		temperature?: number;
+		debug?: boolean;
+		skipValidation?: boolean;
+		validateFn?: (data: unknown) => { isValid: boolean; errors?: string[] };
+	};
+}): Promise<{
+	data: T;
+	cost: { totalTokens: number; costUSD: number };
+}> {
+    // …rest of implementation…
+}
+	const {
+		model = typeof process !== 'undefined' && process.env?.LLM_MODEL
+			? process.env.LLM_MODEL
+			: 'chatgpt-4o-latest',
+		provider = typeof process !== 'undefined' && process.env?.LLM_PROVIDER
+			? process.env.LLM_PROVIDER
+			: 'direct',
+		apiKey,
+		temperature = 0,
+		debug = typeof process !== 'undefined' && process.env?.DEBUG === 'true',
+		skipValidation = false,
+		validateFn
+	} = options;
+
+import { providers } from '../src/lib/utils/openaiApi';
+import { debugLog } from '../src/lib/utils/openaiApi';
+
+	try {
+		// Create function tool with schema
+		const functionTool = {
+			type: 'function' as const,
+			function: {
+				name: functionName,
+				description: `Generate structured data for ${functionName}`,
+				parameters: schema
+			}
+		};
+
+		// For OpenRouter, enhance the messages with schema
+		let enhancedMessages = messages;
+		if (validProvider === 'openrouter') {
+-import { debugLog } from '../src/lib/utils/openaiApi';
++import { debugLog, enhanceMessageWithSchema } from '../src/lib/utils/openaiApi';
+
+		// Configure API call parameters
+		const extendedParams = {
+			model,
+			messages: enhancedMessages,
+			// Include tools for direct OpenAI calls
+			...(validProvider !== 'openrouter'
+				? {
+						tools: [functionTool],
+						tool_choice: { type: 'function', function: { name: functionName } }
+					}
+				: {}),
+			temperature,
+			response_format: { type: 'json_object' }
+		};
+
+		// Make the API call
+// at the top of data-tools/openai.ts
+-import { debugLog } from '../src/lib/utils/openaiApi';
++import { debugLog, callOpenAI } from '../src/lib/utils/openaiApi';
+
+		// Extract JSON from response
+- import { debugLog, callOpenAI } from '../src/lib/utils/openaiApi';
++ import { debugLog, callOpenAI, extractJSONFromLLMResponse } from '../src/lib/utils/openaiApi';
path: data-tools/openai.ts
user: login: Copilot
body: The imported `extractJSONFromLLMResponse` (and potentially `callOpenAI`) isn't used in this scope; remove unused imports to reduce clutter.
```suggestion
+ import { debugLog } from '../src/lib/utils/openaiApi';
```
created_at: 2025-05-15T21:14:13Z
author_association: NONE
diff_hunk: @@ -420,3 +420,107 @@ Keep your response brief and structured according to the function schema.`
 		};
 	}
 }
+
+/**
+ * Function to call OpenAI with a JSON schema for structured responses
+ */
+export async function callOpenAIWithJSONSchema<T>({
+	messages,
+	schema,
+	functionName,
+	options = {}
+}: {
+	messages: ChatCompletionMessageParam[];
+	schema: Record<string, unknown>;
+	functionName: string;
+	options?: {
+		model?: string;
+		provider?: string;
+		apiKey?: string;
+		temperature?: number;
+		debug?: boolean;
+		skipValidation?: boolean;
+		validateFn?: (data: unknown) => { isValid: boolean; errors?: string[] };
+	};
+}): Promise<{
+	data: T;
+	cost: { totalTokens: number; costUSD: number };
+}> {
+    // …rest of implementation…
+}
+	const {
+		model = typeof process !== 'undefined' && process.env?.LLM_MODEL
+			? process.env.LLM_MODEL
+			: 'chatgpt-4o-latest',
+		provider = typeof process !== 'undefined' && process.env?.LLM_PROVIDER
+			? process.env.LLM_PROVIDER
+			: 'direct',
+		apiKey,
+		temperature = 0,
+		debug = typeof process !== 'undefined' && process.env?.DEBUG === 'true',
+		skipValidation = false,
+		validateFn
+	} = options;
+
+import { providers } from '../src/lib/utils/openaiApi';
+import { debugLog } from '../src/lib/utils/openaiApi';
+
+	try {
+		// Create function tool with schema
+		const functionTool = {
+			type: 'function' as const,
+			function: {
+				name: functionName,
+				description: `Generate structured data for ${functionName}`,
+				parameters: schema
+			}
+		};
+
+		// For OpenRouter, enhance the messages with schema
+		let enhancedMessages = messages;
+		if (validProvider === 'openrouter') {
+-import { debugLog } from '../src/lib/utils/openaiApi';
++import { debugLog, enhanceMessageWithSchema } from '../src/lib/utils/openaiApi';
+
+		// Configure API call parameters
+		const extendedParams = {
+			model,
+			messages: enhancedMessages,
+			// Include tools for direct OpenAI calls
+			...(validProvider !== 'openrouter'
+				? {
+						tools: [functionTool],
+						tool_choice: { type: 'function', function: { name: functionName } }
+					}
+				: {}),
+			temperature,
+			response_format: { type: 'json_object' }
+		};
+
+		// Make the API call
+// at the top of data-tools/openai.ts
+-import { debugLog } from '../src/lib/utils/openaiApi';
++import { debugLog, callOpenAI } from '../src/lib/utils/openaiApi';
path: data-tools/openai.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Misplaced import causing syntax error**

This import statement is incorrectly placed in the middle of the function body, causing syntax errors and redeclaration issues.


Move this import to the top of the file with other imports:

```diff
// At the top of the file, around line 23
import { debugLog, callOpenAIWithSchema } from '../src/lib/utils/openaiApi';
+import { callOpenAI } from '../src/lib/utils/openaiApi';
import { providers } from '../src/lib/utils/openaiApi';
import { enhanceMessageWithSchema } from '../src/lib/utils/openaiApi';
import type { ChatCompletionMessageParam } from 'openai';
```


> Committable suggestion skipped: line range outside the PR's diff.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 502-502: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement


(parse)

---

[error] 503-503: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement


(parse)

---

[error] 502-502: Shouldn't redeclare 'debugLog'. Consider to delete it or rename it.

'debugLog' is defined here:


(lint/suspicious/noRedeclare)

---

[error] 503-503: Shouldn't redeclare 'debugLog'. Consider to delete it or rename it.

'debugLog' is defined here:


(lint/suspicious/noRedeclare)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In data-tools/openai.ts at lines 502 to 503, the import statement for debugLog
and callOpenAI is incorrectly placed inside the function body, causing syntax
errors. Move this import statement to the top of the file alongside the other
import statements to fix the syntax error and avoid redeclaration issues.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 24d4e1a
created_at: 2025-05-15T21:16:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -420,3 +420,107 @@ Keep your response brief and structured according to the function schema.`
 		};
 	}
 }
+
+/**
+ * Function to call OpenAI with a JSON schema for structured responses
+ */
+export async function callOpenAIWithJSONSchema<T>({
+	messages,
+	schema,
+	functionName,
+	options = {}
+}: {
+	messages: ChatCompletionMessageParam[];
+	schema: Record<string, unknown>;
+	functionName: string;
+	options?: {
+		model?: string;
+		provider?: string;
+		apiKey?: string;
+		temperature?: number;
+		debug?: boolean;
+		skipValidation?: boolean;
+		validateFn?: (data: unknown) => { isValid: boolean; errors?: string[] };
+	};
+}): Promise<{
+	data: T;
+	cost: { totalTokens: number; costUSD: number };
+}> {
+    // …rest of implementation…
+}
+	const {
+		model = typeof process !== 'undefined' && process.env?.LLM_MODEL
+			? process.env.LLM_MODEL
+			: 'chatgpt-4o-latest',
+		provider = typeof process !== 'undefined' && process.env?.LLM_PROVIDER
+			? process.env.LLM_PROVIDER
+			: 'direct',
+		apiKey,
+		temperature = 0,
+		debug = typeof process !== 'undefined' && process.env?.DEBUG === 'true',
+		skipValidation = false,
+		validateFn
+	} = options;
+
+import { providers } from '../src/lib/utils/openaiApi';
+import { debugLog } from '../src/lib/utils/openaiApi';
+
+	try {
+		// Create function tool with schema
+		const functionTool = {
+			type: 'function' as const,
+			function: {
+				name: functionName,
+				description: `Generate structured data for ${functionName}`,
+				parameters: schema
+			}
+		};
+
+		// For OpenRouter, enhance the messages with schema
+		let enhancedMessages = messages;
+		if (validProvider === 'openrouter') {
+-import { debugLog } from '../src/lib/utils/openaiApi';
++import { debugLog, enhanceMessageWithSchema } from '../src/lib/utils/openaiApi';
+
path: data-tools/openai.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Misplaced import causing syntax error**

This import statement is placed in the middle of the function body, causing a syntax error. It appears to be an attempt to add a missing import referenced earlier at line 481.


Move this import to the top of the file:

```diff
// At the top of the file, around line 23
import { debugLog, callOpenAIWithSchema } from '../src/lib/utils/openaiApi';
import { providers } from '../src/lib/utils/openaiApi';
+import { enhanceMessageWithSchema } from '../src/lib/utils/openaiApi';
import type { ChatCompletionMessageParam } from 'openai';
```

And then use `enhanceMessageWithSchema` in your function implementation where needed.


> Committable suggestion skipped: line range outside the PR's diff.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 482-482: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement


(parse)

---

[error] 483-483: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement


(parse)

---

[error] 483-483: Shouldn't redeclare 'debugLog'. Consider to delete it or rename it.

'debugLog' is defined here:


(lint/suspicious/noRedeclare)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In data-tools/openai.ts around lines 482 to 484, the import statement for
enhanceMessageWithSchema is incorrectly placed inside the function body, causing
a syntax error. Move this import statement to the top of the file with the other
imports. Then, update the function implementation to use
enhanceMessageWithSchema where appropriate.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commits 24d4e1a to bc7e1f4
created_at: 2025-05-15T21:16:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -420,3 +420,107 @@ Keep your response brief and structured according to the function schema.`
 		};
 	}
 }
+
+/**
+ * Function to call OpenAI with a JSON schema for structured responses
+ */
+export async function callOpenAIWithJSONSchema<T>({
+	messages,
+	schema,
+	functionName,
+	options = {}
+}: {
+	messages: ChatCompletionMessageParam[];
+	schema: Record<string, unknown>;
+	functionName: string;
+	options?: {
+		model?: string;
+		provider?: string;
+		apiKey?: string;
+		temperature?: number;
+		debug?: boolean;
+		skipValidation?: boolean;
+		validateFn?: (data: unknown) => { isValid: boolean; errors?: string[] };
+	};
+}): Promise<{
+	data: T;
+	cost: { totalTokens: number; costUSD: number };
+}> {
+    // …rest of implementation…
+}
+	const {
+		model = typeof process !== 'undefined' && process.env?.LLM_MODEL
+			? process.env.LLM_MODEL
+			: 'chatgpt-4o-latest',
+		provider = typeof process !== 'undefined' && process.env?.LLM_PROVIDER
+			? process.env.LLM_PROVIDER
+			: 'direct',
+		apiKey,
+		temperature = 0,
+		debug = typeof process !== 'undefined' && process.env?.DEBUG === 'true',
+		skipValidation = false,
+		validateFn
+	} = options;
+
+import { providers } from '../src/lib/utils/openaiApi';
+import { debugLog } from '../src/lib/utils/openaiApi';
+
+	try {
+		// Create function tool with schema
+		const functionTool = {
+			type: 'function' as const,
+			function: {
+				name: functionName,
+				description: `Generate structured data for ${functionName}`,
+				parameters: schema
+			}
+		};
+
+		// For OpenRouter, enhance the messages with schema
+		let enhancedMessages = messages;
+		if (validProvider === 'openrouter') {
+-import { debugLog } from '../src/lib/utils/openaiApi';
++import { debugLog, enhanceMessageWithSchema } from '../src/lib/utils/openaiApi';
+
+		// Configure API call parameters
+		const extendedParams = {
+			model,
+			messages: enhancedMessages,
+			// Include tools for direct OpenAI calls
+			...(validProvider !== 'openrouter'
+				? {
+						tools: [functionTool],
+						tool_choice: { type: 'function', function: { name: functionName } }
+					}
+				: {}),
+			temperature,
+			response_format: { type: 'json_object' }
+		};
+
+		// Make the API call
+// at the top of data-tools/openai.ts
+-import { debugLog } from '../src/lib/utils/openaiApi';
++import { debugLog, callOpenAI } from '../src/lib/utils/openaiApi';
+
+		// Extract JSON from response
+- import { debugLog, callOpenAI } from '../src/lib/utils/openaiApi';
++ import { debugLog, callOpenAI, extractJSONFromLLMResponse } from '../src/lib/utils/openaiApi';
path: data-tools/openai.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Misplaced import causing syntax error**

Another import statement in the middle of function implementation, causing syntax errors and conflicts with previous imports.


Move this import to the top of the file and consolidate it with other imports from the same module:

```diff
// At the top of the file, around line 23
-import { debugLog, callOpenAIWithSchema } from '../src/lib/utils/openaiApi';
+import { 
+  debugLog, 
+  callOpenAIWithSchema, 
+  callOpenAI, 
+  enhanceMessageWithSchema, 
+  extractJSONFromLLMResponse,
+  providers 
+} from '../src/lib/utils/openaiApi';
-import { callOpenAI } from '../src/lib/utils/openaiApi';
-import { providers } from '../src/lib/utils/openaiApi';
-import { enhanceMessageWithSchema } from '../src/lib/utils/openaiApi';
import type { ChatCompletionMessageParam } from 'openai';
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
// ------------------------------------
// At the very top of data-tools/openai.ts
// ------------------------------------
import {
  debugLog,
  callOpenAIWithSchema,
  callOpenAI,
  enhanceMessageWithSchema,
  extractJSONFromLLMResponse,
  providers,
} from '../src/lib/utils/openaiApi';

import type { ChatCompletionMessageParam } from 'openai';

// ... rest of your code ...
```

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 506-506: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement


(parse)

---

[error] 507-507: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement


(parse)

---

[error] 506-506: Shouldn't redeclare 'debugLog'. Consider to delete it or rename it.

'debugLog' is defined here:


(lint/suspicious/noRedeclare)

---

[error] 506-506: Shouldn't redeclare 'callOpenAI'. Consider to delete it or rename it.

'callOpenAI' is defined here:


(lint/suspicious/noRedeclare)

---

[error] 507-507: Shouldn't redeclare 'debugLog'. Consider to delete it or rename it.

'debugLog' is defined here:


(lint/suspicious/noRedeclare)

---

[error] 507-507: Shouldn't redeclare 'callOpenAI'. Consider to delete it or rename it.

'callOpenAI' is defined here:


(lint/suspicious/noRedeclare)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In data-tools/openai.ts around lines 506 to 507, the import statement for
extractJSONFromLLMResponse is placed in the middle of the function
implementation, causing syntax errors. Move this import statement to the top of
the file and combine it with the existing import statement from
'../src/lib/utils/openaiApi' to consolidate all imports from that module in one
place.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T21:16:06Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-05-15T20:53:11Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Summary by CodeRabbit

- **New Features**
  - Introduced a new function for calling the OpenAI API with structured JSON responses validated against a provided schema.

- **Refactor**
  - Enhanced internal API call options for company analysis tools to support more flexible tool selection.
## Summary by CodeRabbit

- **New Features**
  - Introduced a new function for making schema-driven, structured API calls to OpenAI, supporting customizable validation and provider options.

- **Chores**
  - Updated internal options for company analysis processing to enhance compatibility with function-based tools.

## Walkthrough

A new utility function, `callOpenAIWithJSONSchema`, was introduced to provide a schema-driven interface for structured OpenAI API calls. Additionally, the `analyzeCompany` function in the company analysis module was updated to include a `tool_choice` parameter when invoking `processCompanyAnalysis`. No exported function signatures were altered.

## Changes

| File(s)                       | Change Summary                                                                                                         |
|------------------------------|------------------------------------------------------------------------------------------------------------------------|
| data-tools/openai.ts          | Added `callOpenAIWithJSONSchema`, an exported async function for schema-based, validated OpenAI API calls with flexible options. |
| data-tools/companyAnalysis.ts | Modified the call to `processCompanyAnalysis` within `analyzeCompany` to include a `tool_choice` parameter.             |

## Poem

> In the warren of code, a new tool appears,  
> Schema-driven calls to OpenAI—cheers!  
> Company analysis gets a new choice,  
> Rabbits rejoice with a hop and a voice.  
> Structured and tidy, our functions now gleam,  
> Hopping ahead on the data stream!  
> 🐇✨

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- DwQgtGAEAqAWCWBnSTIEMB26CuAXA9mAOYCmGJATmriQCaQDG+Ats2bgFyQAOFk+AIwBWJBrngA3EsgEBPRvlqU0AgfFwA6NPEgQAfACgjoCEYDEZyAAUASpETZWaCrKNxU3bABsvkCiQBHbGlcABpIcVwvOkgAIgAxeAAPSAFsIhQsAnwvAH0GWHx4BhIeZzQ2GgpQ2PRaWn9ERGl0SGYkRHgMDP8g+H96bnLKykhKCnw+fy9qGIII2FKAA2y8gqKSjVxZbhIlsqoRig0YRcZYTFJkLoYvbCVIbG5aWeR53AB3fEgAM3hoxAcIxQACSWAAVOCXrg0GBVogAPRMZhDDCyACCGDQXlknUQW0QkPCuDOS0w2NkAC8SABhFio2T7H7YDBieD4LCoZiKeB/ObfG53B4k5arfKFYp7fjCUS4SAfdSwVorHZS3j4XYUbb2Ehy+ZLADkzNZ4g5BqWGgMoIhUOosPhCI1ZG0BKJC2WDGxXgA8rsMOiQQB1RUAZQKJGYaCZLLZHJQyCe0P5dXoIsgK3wOXFGxIW1V+yGh11o0w9BISW4XmK6hxOrl6gi30NxtjGHNloMcGkpW5tF5xWo7IwyG0zEbfmkOSk7rGFAmfDkY2H2AoXQyad62H6MQzWfWkrzuwLw2LfA8E01taUf3I9FLZSaMS6M+mJAkmDlLdNw5O6Pq6iHbFICUGF/hHDB6CYDAaCSOVPSwARSh+fAWXoZ80yrDAAGsYjpbh/nwOVA0mLDECGEodSaIcOwsSA6VYdhkAcJwXDcM5PB8CcghCCcSkkGI0AnCR4BID4uOCRBPwmMc0zQPBCmqSBgx8eAKnRABxAAJcI+38MRkzTbBmj4JYmCUKhVHUbQLUgMEJzIjlmnCUzFGUSzQP2AAKTAcAISMaHoARCIASgiVciFIAZWn8YTRJ4CYSiaE4u1SQiDWQfxK3kD40HA3zCFIcgqACtppEQNBSAUDA/godpugWagXzfESxJy64MAA2ZaBOEE5WxRBvgwQi5guOU6SUGwVDUes8pufw2GgoCYpa+xcRoGTRuA/AWiGuV/DAZa4uxfw0FoeRDo+GJkXaXBwNTTaN2auLrvvVAOVrNBuErAcBGieVFiwOS/MHBghJakd/DKIy6A7AA5b5mS1RY+Guxjwh+Eg6AENAGCw8JJmApAGCMzo40uyH1WEpR6DjNMON8TcQmSs4uiqHHv3lXLICrG6DJZjrxCWp6xMZyT0AgmcgZYLr0FxoaPmiWgiAWuV8B+JrYrE3Bwsijt9GMcAoDIGn1al4gyGUErUegrheGlEQ2SkGR5DMtzpq0HR9ZMKB3GuPKzcKy2rpYFWuCoMTmMjFxUhd1yLPd7RdDAQwDdMAxoXtTMvERZEGUxCk8QJIFYhLgxaIDc2iplyPnHkNXzkuaQ2NKT1OP1dVEsQei86xHE8X2BUSXQ0lyRxalu8wRlfhjDm2raHk+VTAVWSFUofNOvtvyAjUOd3NYJRKfZmj1JsAG8IlVLgjRnocDUgABfGzff4bhZ65jeBK8DkiE6YUznLJA4h6pLEksVEgRAp6FgqKecIHwEAFAnJGLoyAoKb0Ar4WmpJoYAFlvDiBDDQbgTIZhEBOPDfgIoUYXG6C0d4ZwoLaxyL8L+HxwjjAJtQ2gmEiD4z4ChXAnhPyTH8kAjI5MeynVzEYcwlh0ReDZt+N43w0xKFuOURR/B1blm4JMEqBNPC/WKEuSIIlEBGHIQURuyBxFtEkeONMnQiBYlwCuFo9dtG6JiPo7AhjQbsAAtIZmpQPFahiF+IcyAyS9ypLSekk99j3iWLQRw3AwyLEjNAfAiRoj7H8KBLIix+j8HCl0ICjjnGuPxAYEusRgRpwznCLOiInRYngEXappdy4gkrkHegNdo710sTQsxnYzh9h+OrfwPx2aTDumMWCxs3ikgaQ6FpLpbr7F7N4Uoi4N5rlaOQMSISSq5VkKyWAEwhpGWniaIc6ZW4+j9AGYMJIABSIZvSwzSRGKMzNUDhLjJTeASgRyQEDquUGrNKDTIovMB5M5fRkADJAdEVgQTykVPYbW2AxCuPoO8z59kdHDhaO+KsSY7xEG0MOPq8V8BUxiAS2G9hwyRh6n1BgJRX5vFVIME8VR2q3HuPsyxco2BNAqtIcIgkmUsvSWgaVNzWyQCxGwaVEsd7oKqn8IgK5BxxgcAgrmvYSBeHCEC8y4RUXopwrIYkEZNTUFcTpEgaQMg/BIeEMlwL9VYEQFheAX01zqrvIwIyflIDeuhHcgFGAglKo5iBSgdUWjXjkvI+eShfD3gtaMMlElfjSSXMJS5KtI3OFUr9NxfBpk+BkDjLC44LgUFoK7egaa8H4lsnBRy2KxCgtjY2JhggHZygjIhf89UHGssEveQdqqSAht+ATNMsQWkTDwJQWoubFINjINQxKpUJVXAxSSGciAZ0AG4KHIwVE5FA9YV73FoWcQdqxxZLyYfuCiw9SjWsYF6A40CBXsoA3W1oLDKBgGiFIXwpkvSIv9CCfYeB/jqGypikUzQgNHDmeWbW7NkCyp+EWx6DkSU9VNuW8lvqE13PPPS4FAkJZRto6gXa9gA1fToOEBsrGaBLNKIWZo7a7Qfu5vgH+4t5D8buewig1x1ZKDdUQfZqBnRVu6qcJCN84x5JXMOGcwmYgZwvrsO8yBwVGKWNABJX96qDyVEwMWlRTp2hOAAUTnLM4CK59l0wSmVdAkNPTpFgGECTEU+Yt0cjkRd4m8kXPwB8ONRhvSUIbsMzIQqHh1QJshWq1BRF1CEOGlWcyoJ4hoNBTIShFocxxhMJoM4/jRDId8IZJ6bGRmFN8ABkl9mDq/qp0GBMGETF8B65LmR0wdzKhPNE+c+5ICWM5ObTQQxrmiAQkg3Alu4hW2twLTRcHyPgDtvb0T+68PTKQXAAAhXKxQFuyDBMhJ+ZxDl0bjKgT6fpqboD1P/DV6s0ytakdI8u8jLYRPsWM0QMxiqw/cRWTxNM+AGKrH46CASRlQHiLpwG9Q6BcEgAYSA6ZjnoEQGc0Gg6HmIeeYqJl3zIzAGgHoTyUDmCAkgOfcV5UrhcBpKNbu0RvzYLKpKqwwwADaABda9F75VcBsKISYtBgCgODY8bC8sMB6GvbG2G0CuDa+6NezVjkAD8XB+euS8LbrFq4Ld0oZRQJ35uiDXs+vAAA0iQWQnvtZrmvetR1Lj/BO4wI4RCFBr3KfSE7oKcXMBK64wANWxD678yes4kDT9RnPJB4gYCd55DOXAWRYX16FAAvHoPn8Ys/kq4Cn6Ihf5OIGDy7ogCuH4D/vsFLgVhpJIBIMAc+leYDXuc5wJvBAYReEyThYcXAY/MDj7P/AkkACqIYAAi6/Y+jHvg/PQ+xnxJLtI0nIzS/TrMQEsDptSrTJ1TkbEH+UelgMgiHdgYcaAEcjgUc8gi4rs8c6gHsSchg3sCgrA6guQwKiAuQF0dAuQkkzgco+sBgcBAADD8AAGyEEAAcDAAAzAAJwMA/AADsAATAXhMvQZQQAIz0G0CUG0AAAs5BDAAgtBJBtBPw5BoheB9BaAJBPwoMXshskAhBrBaAaAAArFwRjFwWgGwT8JQRMngXgawQQbQAIMoSQXgTQbQAYZQWgIQeQUoaULIRAKkAwLQSQKwT8LwSUFIWgNwZQdwSQdjD4YQeIdMi4QXsoTYYoYQbQLQcobQTAR/vATdEgbQCgWgbQLkMbDAUAA= -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=WilliamAGH/company-lookup-tool&utm_content=1):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` or `merge` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: WilliamAGH
created_at: 2025-05-15T21:25:56Z
body: @coderabbitai review
author_association: OWNER
user: login: coderabbitai[bot]
created_at: 2025-05-15T21:26:02Z
body: <!-- This is an auto-generated reply by CodeRabbit -->
<details>
<summary>✅ Actions performed</summary>

Review triggered.

> Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

</details>
author_association: CONTRIBUTOR