AI Analysis and Content Strategy fixes. Enhanced Strategy Routes refactoring.
This commit is contained in:
@@ -332,9 +332,9 @@ export const billingService = {
|
||||
// Debug: Log cost calculation details
|
||||
console.log('💰 [BILLING DEBUG] Cost calculation:', {
|
||||
backendTotalCost: coerced.current_usage.total_cost,
|
||||
geminiCost: coerced.current_usage.provider_breakdown.gemini.cost,
|
||||
huggingfaceCost: coerced.current_usage.provider_breakdown.huggingface.cost,
|
||||
calculatedTotal: coerced.current_usage.provider_breakdown.gemini.cost + coerced.current_usage.provider_breakdown.huggingface.cost,
|
||||
geminiCost: coerced.current_usage.provider_breakdown.gemini?.cost ?? 0,
|
||||
huggingfaceCost: coerced.current_usage.provider_breakdown.huggingface?.cost ?? 0,
|
||||
calculatedTotal: (coerced.current_usage.provider_breakdown.gemini?.cost ?? 0) + (coerced.current_usage.provider_breakdown.huggingface?.cost ?? 0),
|
||||
providerBreakdown: coerced.current_usage.provider_breakdown,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { apiClient, aiApiClient, pollingApiClient } from "../api/client";
|
||||
// Import research types for use in this file
|
||||
import type { ResearchMode, ResearchProvider, SourceType, DateRange, ResearchSource, ResearchConfig, ResearchResponse } from './researchApi';
|
||||
// Re-export research types for backward compatibility
|
||||
// TODO: Update all blog writer code to import from researchApi.ts directly
|
||||
export type { ResearchMode, ResearchProvider, SourceType, DateRange, ResearchSource, ResearchConfig, ResearchResponse } from './researchApi';
|
||||
|
||||
export interface PersonaInfo {
|
||||
persona_id?: string;
|
||||
@@ -7,54 +12,6 @@ export interface PersonaInfo {
|
||||
industry?: string;
|
||||
}
|
||||
|
||||
export interface ResearchSource {
|
||||
title: string;
|
||||
url: string;
|
||||
excerpt?: string;
|
||||
credibility_score?: number;
|
||||
published_at?: string;
|
||||
index?: number;
|
||||
source_type?: string;
|
||||
}
|
||||
|
||||
export type ResearchMode = 'basic' | 'comprehensive' | 'targeted';
|
||||
export type ResearchProvider = 'google' | 'exa' | 'tavily';
|
||||
export type SourceType = 'web' | 'academic' | 'news' | 'industry' | 'expert';
|
||||
export type DateRange = 'last_week' | 'last_month' | 'last_3_months' | 'last_6_months' | 'last_year' | 'all_time';
|
||||
|
||||
export interface ResearchConfig {
|
||||
mode?: ResearchMode;
|
||||
provider?: ResearchProvider;
|
||||
date_range?: DateRange;
|
||||
source_types?: SourceType[];
|
||||
max_sources?: number;
|
||||
include_statistics?: boolean;
|
||||
include_expert_quotes?: boolean;
|
||||
include_competitors?: boolean;
|
||||
include_trends?: boolean;
|
||||
// Exa-specific options
|
||||
exa_category?: string;
|
||||
exa_include_domains?: string[];
|
||||
exa_exclude_domains?: string[];
|
||||
exa_search_type?: 'auto' | 'keyword' | 'neural';
|
||||
// Tavily-specific options
|
||||
tavily_topic?: 'general' | 'news' | 'finance';
|
||||
tavily_search_depth?: 'basic' | 'advanced';
|
||||
tavily_include_domains?: string[];
|
||||
tavily_exclude_domains?: string[];
|
||||
tavily_include_answer?: boolean | 'basic' | 'advanced';
|
||||
tavily_include_raw_content?: boolean | 'markdown' | 'text';
|
||||
tavily_include_images?: boolean;
|
||||
tavily_include_image_descriptions?: boolean;
|
||||
tavily_include_favicon?: boolean;
|
||||
tavily_time_range?: 'day' | 'week' | 'month' | 'year' | 'd' | 'w' | 'm' | 'y';
|
||||
tavily_start_date?: string; // YYYY-MM-DD
|
||||
tavily_end_date?: string; // YYYY-MM-DD
|
||||
tavily_country?: string;
|
||||
tavily_chunks_per_source?: number; // 1-3
|
||||
tavily_auto_parameters?: boolean;
|
||||
}
|
||||
|
||||
export interface BlogResearchRequest {
|
||||
keywords: string[];
|
||||
topic?: string;
|
||||
@@ -98,18 +55,10 @@ export interface GroundingMetadata {
|
||||
web_search_queries: string[];
|
||||
}
|
||||
|
||||
export interface BlogResearchResponse {
|
||||
success: boolean;
|
||||
keywords?: string[];
|
||||
sources: ResearchSource[];
|
||||
keyword_analysis: Record<string, any>;
|
||||
competitor_analysis: Record<string, any>;
|
||||
suggested_angles: string[];
|
||||
export interface BlogResearchResponse extends ResearchResponse {
|
||||
// Blog Writer specific extensions
|
||||
search_widget?: string;
|
||||
search_queries?: string[];
|
||||
grounding_metadata?: GroundingMetadata;
|
||||
original_keywords?: string[]; // Original user-provided keywords for caching
|
||||
error_message?: string;
|
||||
}
|
||||
|
||||
export interface BlogOutlineSection {
|
||||
|
||||
@@ -195,6 +195,8 @@ class ContentPlanningAPI {
|
||||
}
|
||||
|
||||
async getStrategies(userId?: number) {
|
||||
// Note: apiClient interceptor handles authentication token injection
|
||||
// ProtectedRoute ensures user is authenticated before component renders
|
||||
const params = userId ? { user_id: userId } : {};
|
||||
const response = await apiClient.get(`${this.baseURL}/enhanced-strategies`, { params });
|
||||
return response.data?.data || response.data;
|
||||
@@ -687,7 +689,21 @@ class ContentPlanningAPI {
|
||||
|
||||
// SSE Methods (for Orchestrator - real-time updates needed)
|
||||
async streamStrategicIntelligence(userId?: number): Promise<EventSource> {
|
||||
const url = `${this.baseURL}/enhanced-strategies/stream/strategic-intelligence?user_id=${userId || 1}`;
|
||||
// Check if auth token is available before making request
|
||||
const { getAuthTokenGetter } = await import('../api/client');
|
||||
const tokenGetter = getAuthTokenGetter();
|
||||
|
||||
if (!tokenGetter) {
|
||||
throw new Error('Authentication not ready. Please wait for sign-in to complete.');
|
||||
}
|
||||
|
||||
const token = await tokenGetter();
|
||||
if (!token) {
|
||||
throw new Error('Authentication required. Please sign in to access content planning features.');
|
||||
}
|
||||
|
||||
// EventSource doesn't support custom headers, so we pass token as query parameter
|
||||
const url = `${this.baseURL}/enhanced-strategies/stream/strategic-intelligence?user_id=${userId || 1}&token=${encodeURIComponent(token)}`;
|
||||
return new EventSource(url);
|
||||
}
|
||||
|
||||
@@ -860,7 +876,21 @@ class ContentPlanningAPI {
|
||||
|
||||
// Additional SSE Methods (for other features that need real-time updates)
|
||||
async streamKeywordResearch(userId?: number): Promise<EventSource> {
|
||||
const url = `${this.baseURL}/enhanced-strategies/stream/keyword-research?user_id=${userId || 1}`;
|
||||
// Check if auth token is available before making request
|
||||
const { getAuthTokenGetter } = await import('../api/client');
|
||||
const tokenGetter = getAuthTokenGetter();
|
||||
|
||||
if (!tokenGetter) {
|
||||
throw new Error('Authentication not ready. Please wait for sign-in to complete.');
|
||||
}
|
||||
|
||||
const token = await tokenGetter();
|
||||
if (!token) {
|
||||
throw new Error('Authentication required. Please sign in to access content planning features.');
|
||||
}
|
||||
|
||||
// EventSource doesn't support custom headers, so we pass token as query parameter
|
||||
const url = `${this.baseURL}/enhanced-strategies/stream/keyword-research?user_id=${userId || 1}&token=${encodeURIComponent(token)}`;
|
||||
return new EventSource(url);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,9 +23,10 @@ const MONITORING_BASE_URL = API_BASE_URL
|
||||
: '/api/content-planning/monitoring';
|
||||
|
||||
// Create axios instance for monitoring APIs
|
||||
// Use shorter timeout for health checks to prevent blocking dashboard
|
||||
const monitoringAPI = axios.create({
|
||||
baseURL: MONITORING_BASE_URL,
|
||||
timeout: 10000,
|
||||
timeout: 5000, // Reduced from 10000 to 5000ms for faster failure
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
|
||||
116
frontend/src/services/researchApi.ts
Normal file
116
frontend/src/services/researchApi.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Research API Types and Interfaces
|
||||
*
|
||||
* Dedicated types for the Research Engine, separate from Blog Writer.
|
||||
* This ensures proper separation of concerns.
|
||||
*
|
||||
* Author: ALwrity Team
|
||||
* Version: 1.0
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// Core Research Types
|
||||
// ============================================================================
|
||||
|
||||
export type ResearchMode = 'basic' | 'comprehensive' | 'targeted';
|
||||
export type ResearchProvider = 'google' | 'exa' | 'tavily';
|
||||
export type SourceType = 'web' | 'academic' | 'news' | 'industry' | 'expert';
|
||||
export type DateRange = 'last_week' | 'last_month' | 'last_3_months' | 'last_6_months' | 'last_year' | 'all_time';
|
||||
|
||||
// ============================================================================
|
||||
// Research Source
|
||||
// ============================================================================
|
||||
|
||||
export interface ResearchSource {
|
||||
title: string;
|
||||
url: string;
|
||||
excerpt?: string;
|
||||
credibility_score?: number;
|
||||
published_at?: string;
|
||||
index?: number;
|
||||
source_type?: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Research Configuration
|
||||
// ============================================================================
|
||||
|
||||
export interface ResearchConfig {
|
||||
mode?: ResearchMode;
|
||||
provider?: ResearchProvider;
|
||||
date_range?: DateRange;
|
||||
source_types?: SourceType[];
|
||||
max_sources?: number;
|
||||
include_statistics?: boolean;
|
||||
include_expert_quotes?: boolean;
|
||||
include_competitors?: boolean;
|
||||
include_trends?: boolean;
|
||||
|
||||
// Exa-specific options
|
||||
exa_category?: string;
|
||||
exa_include_domains?: string[];
|
||||
exa_exclude_domains?: string[];
|
||||
exa_search_type?: 'auto' | 'keyword' | 'neural' | 'fast' | 'deep';
|
||||
exa_num_results?: number;
|
||||
exa_date_filter?: string; // ISO date string for startPublishedDate (e.g., "2025-01-01T00:00:00.000Z")
|
||||
exa_end_published_date?: string; // ISO date string for endPublishedDate
|
||||
exa_start_crawl_date?: string; // ISO date string for startCrawlDate
|
||||
exa_end_crawl_date?: string; // ISO date string for endCrawlDate
|
||||
exa_include_text?: string[]; // Text that must be present in webpage text (max 1 string, up to 5 words)
|
||||
exa_exclude_text?: string[]; // Text that must not be present in webpage text (max 1 string, up to 5 words)
|
||||
exa_highlights?: boolean;
|
||||
exa_highlights_num_sentences?: number; // Number of sentences per highlight (default: 2)
|
||||
exa_highlights_per_url?: number; // Number of highlights per URL (default: 3)
|
||||
exa_context?: boolean | { maxCharacters?: number }; // Context string: boolean or object with maxCharacters
|
||||
exa_context_max_characters?: number; // Max characters for context string (recommended: 10000+)
|
||||
exa_text_max_characters?: number; // Max characters for full page text (default: 1000)
|
||||
exa_summary_query?: string; // Custom query for LLM-generated summary
|
||||
exa_additional_queries?: string[]; // Additional query variations for Deep search (only works with type="deep")
|
||||
|
||||
// Tavily-specific options
|
||||
tavily_topic?: 'general' | 'news' | 'finance';
|
||||
tavily_search_depth?: 'basic' | 'advanced';
|
||||
tavily_include_domains?: string[];
|
||||
tavily_exclude_domains?: string[];
|
||||
tavily_include_answer?: boolean | 'basic' | 'advanced';
|
||||
tavily_include_raw_content?: boolean | 'markdown' | 'text';
|
||||
tavily_include_images?: boolean;
|
||||
tavily_include_image_descriptions?: boolean;
|
||||
tavily_include_favicon?: boolean;
|
||||
tavily_time_range?: 'day' | 'week' | 'month' | 'year' | 'd' | 'w' | 'm' | 'y';
|
||||
tavily_start_date?: string; // YYYY-MM-DD
|
||||
tavily_end_date?: string; // YYYY-MM-DD
|
||||
tavily_country?: string;
|
||||
tavily_chunks_per_source?: number; // 1-3
|
||||
tavily_auto_parameters?: boolean;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Research Response (Generic)
|
||||
// ============================================================================
|
||||
|
||||
export interface ResearchResponse {
|
||||
success: boolean;
|
||||
keywords?: string[];
|
||||
sources: ResearchSource[];
|
||||
keyword_analysis: Record<string, any>;
|
||||
competitor_analysis: Record<string, any>;
|
||||
suggested_angles: string[];
|
||||
search_queries?: string[];
|
||||
original_keywords?: string[];
|
||||
error_message?: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Research Request
|
||||
// ============================================================================
|
||||
|
||||
export interface ResearchRequest {
|
||||
keywords: string[];
|
||||
topic?: string;
|
||||
industry?: string;
|
||||
target_audience?: string;
|
||||
tone?: string;
|
||||
research_mode?: ResearchMode;
|
||||
config?: ResearchConfig;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { apiClient } from '../api/client';
|
||||
import { ResearchConfig, BlogResearchResponse } from './blogWriterApi';
|
||||
import { ResearchConfig, ResearchResponse } from './researchApi';
|
||||
|
||||
export interface ResearchEngineRequest {
|
||||
query: string;
|
||||
@@ -25,12 +25,11 @@ export interface ResearchEngineRequest {
|
||||
tavily_include_raw_content?: boolean | string;
|
||||
tavily_time_range?: string;
|
||||
tavily_country?: string;
|
||||
config?: ResearchConfig; // keep compatibility
|
||||
config?: ResearchConfig;
|
||||
}
|
||||
|
||||
export interface ResearchEngineResponse extends BlogResearchResponse {
|
||||
export interface ResearchEngineResponse extends ResearchResponse {
|
||||
provider_used?: string;
|
||||
search_queries?: string[];
|
||||
}
|
||||
|
||||
export interface ResearchTaskResponse {
|
||||
|
||||
Reference in New Issue
Block a user