feat(research): Implement research APIs.
This commit is contained in:
@@ -233,52 +233,37 @@ export class OpenAiProvider extends MultiModalModel {
|
||||
}
|
||||
|
||||
public async research(optionsArg: ResearchOptions): Promise<ResearchResponse> {
|
||||
// Determine which model to use based on search depth
|
||||
// Determine which model to use - Deep Research API requires specific models
|
||||
let model: string;
|
||||
if (optionsArg.searchDepth === 'deep') {
|
||||
model = this.options.researchModel || 'o4-mini-deep-research-2025-06-26';
|
||||
} else {
|
||||
model = this.options.chatModel || 'gpt-5-mini';
|
||||
// For basic/advanced, still use deep research models if web search is needed
|
||||
if (optionsArg.includeWebSearch) {
|
||||
model = this.options.researchModel || 'o4-mini-deep-research-2025-06-26';
|
||||
} else {
|
||||
model = this.options.chatModel || 'gpt-5-mini';
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare the request parameters
|
||||
const systemMessage = 'You are a research assistant. Provide comprehensive answers with citations and sources when available.';
|
||||
|
||||
// Prepare request parameters using Deep Research API format
|
||||
const requestParams: any = {
|
||||
model,
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: 'You are a research assistant. Provide comprehensive answers with citations and sources when available.'
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: optionsArg.query
|
||||
}
|
||||
],
|
||||
temperature: 0.7
|
||||
instructions: systemMessage,
|
||||
input: optionsArg.query
|
||||
};
|
||||
|
||||
// Add web search tools if requested
|
||||
// Add web search tool if requested
|
||||
if (optionsArg.includeWebSearch || optionsArg.searchDepth === 'deep') {
|
||||
requestParams.tools = [
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'web_search',
|
||||
description: 'Search the web for information',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
query: {
|
||||
type: 'string',
|
||||
description: 'The search query'
|
||||
}
|
||||
},
|
||||
required: ['query']
|
||||
}
|
||||
}
|
||||
type: 'web_search_preview',
|
||||
search_context_size: optionsArg.searchDepth === 'deep' ? 'high' :
|
||||
optionsArg.searchDepth === 'advanced' ? 'medium' : 'low'
|
||||
}
|
||||
];
|
||||
requestParams.tool_choice = 'auto';
|
||||
}
|
||||
|
||||
// Add background flag for deep research
|
||||
@@ -287,14 +272,36 @@ export class OpenAiProvider extends MultiModalModel {
|
||||
}
|
||||
|
||||
try {
|
||||
// Execute the research request
|
||||
const result = await this.openAiApiClient.chat.completions.create(requestParams);
|
||||
// Execute the research request using Deep Research API
|
||||
const result = await this.openAiApiClient.responses.create(requestParams);
|
||||
|
||||
// Extract the answer
|
||||
const answer = result.choices[0].message.content || '';
|
||||
|
||||
// Parse sources from the response (OpenAI often includes URLs in markdown format)
|
||||
// Extract the answer from output items
|
||||
let answer = '';
|
||||
const sources: Array<{ url: string; title: string; snippet: string }> = [];
|
||||
const searchQueries: string[] = [];
|
||||
|
||||
// Process output items
|
||||
for (const item of result.output || []) {
|
||||
// Extract message content
|
||||
if (item.type === 'message' && 'content' in item) {
|
||||
const messageItem = item as any;
|
||||
for (const contentItem of messageItem.content || []) {
|
||||
if (contentItem.type === 'output_text' && 'text' in contentItem) {
|
||||
answer += contentItem.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract web search queries
|
||||
if (item.type === 'web_search_call' && 'action' in item) {
|
||||
const searchItem = item as any;
|
||||
if (searchItem.action && searchItem.action.type === 'search' && 'query' in searchItem.action) {
|
||||
searchQueries.push(searchItem.action.query);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse sources from markdown links in the answer
|
||||
const urlRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
@@ -302,27 +309,10 @@ export class OpenAiProvider extends MultiModalModel {
|
||||
sources.push({
|
||||
title: match[1],
|
||||
url: match[2],
|
||||
snippet: '' // OpenAI doesn't provide snippets in standard responses
|
||||
snippet: ''
|
||||
});
|
||||
}
|
||||
|
||||
// Extract search queries if tools were used
|
||||
const searchQueries: string[] = [];
|
||||
if (result.choices[0].message.tool_calls) {
|
||||
for (const toolCall of result.choices[0].message.tool_calls) {
|
||||
if ('function' in toolCall && toolCall.function.name === 'web_search') {
|
||||
try {
|
||||
const args = JSON.parse(toolCall.function.arguments);
|
||||
if (args.query) {
|
||||
searchQueries.push(args.query);
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore parsing errors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
answer,
|
||||
sources,
|
||||
|
Reference in New Issue
Block a user