forked from 0xWheatyz/SPARC
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 44620614b6 | |||
| c72a44aa56 | |||
| 6aa71eb17e | |||
| fb52d08387 | |||
| 223d5f7e5d |
@@ -39,6 +39,12 @@ jobs:
|
|||||||
apk add --no-cache nodejs npm
|
apk add --no-cache nodejs npm
|
||||||
cd frontend
|
cd frontend
|
||||||
npm ci
|
npm ci
|
||||||
|
npm run generate:local
|
||||||
|
if ! git diff --quiet src/api/schema.d.ts; then
|
||||||
|
echo "ERROR: src/api/schema.d.ts is out of date. Run 'npm run generate:local' and commit the result."
|
||||||
|
git diff src/api/schema.d.ts
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
npx tsc --noEmit
|
npx tsc --noEmit
|
||||||
|
|
||||||
- name: Run pytest
|
- name: Run pytest
|
||||||
|
|||||||
@@ -40,6 +40,16 @@ jobs:
|
|||||||
apk add --no-cache nodejs npm
|
apk add --no-cache nodejs npm
|
||||||
cd frontend && npm ci
|
cd frontend && npm ci
|
||||||
|
|
||||||
|
- name: Verify generated API types are up to date
|
||||||
|
shell: sh
|
||||||
|
run: |
|
||||||
|
cd frontend && npm run generate:local
|
||||||
|
if ! git diff --quiet src/api/schema.d.ts; then
|
||||||
|
echo "ERROR: src/api/schema.d.ts is out of date. Run 'npm run generate:local' and commit the result."
|
||||||
|
git diff src/api/schema.d.ts
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Run TypeScript type check
|
- name: Run TypeScript type check
|
||||||
shell: sh
|
shell: sh
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
+12
-7
@@ -33,7 +33,7 @@ class CompanyAnalyzer:
|
|||||||
self.db.connect()
|
self.db.connect()
|
||||||
self.db.initialize_schema()
|
self.db.initialize_schema()
|
||||||
|
|
||||||
def analyze_company(self, company_name: str, patents: "Patents | None" = None) -> str:
|
def analyze_company(self, company_name: str, patents: "Patents | None" = None, model: str | None = None) -> str:
|
||||||
"""Analyze a company's performance based on their patent portfolio.
|
"""Analyze a company's performance based on their patent portfolio.
|
||||||
|
|
||||||
This is the main entry point that orchestrates the full pipeline:
|
This is the main entry point that orchestrates the full pipeline:
|
||||||
@@ -46,6 +46,7 @@ class CompanyAnalyzer:
|
|||||||
Args:
|
Args:
|
||||||
company_name: Name of the company to analyze
|
company_name: Name of the company to analyze
|
||||||
patents: Optional pre-fetched Patents result to avoid duplicate API calls
|
patents: Optional pre-fetched Patents result to avoid duplicate API calls
|
||||||
|
model: Optional LLM model override (e.g. 'openai/gpt-4o')
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Comprehensive analysis of company's innovation and performance outlook
|
Comprehensive analysis of company's innovation and performance outlook
|
||||||
@@ -100,12 +101,12 @@ class CompanyAnalyzer:
|
|||||||
|
|
||||||
# Analyze the full portfolio with LLM
|
# Analyze the full portfolio with LLM
|
||||||
analysis = self.llm_analyzer.analyze_patent_portfolio(
|
analysis = self.llm_analyzer.analyze_patent_portfolio(
|
||||||
patents_data=processed_patents, company_name=company_name
|
patents_data=processed_patents, company_name=company_name, model=model
|
||||||
)
|
)
|
||||||
|
|
||||||
return analysis
|
return analysis
|
||||||
|
|
||||||
def analyze_single_patent(self, patent_id: str, company_name: str) -> str:
|
def analyze_single_patent(self, patent_id: str, company_name: str, model: str | None = None) -> str:
|
||||||
"""Analyze a single patent by ID.
|
"""Analyze a single patent by ID.
|
||||||
|
|
||||||
If the patent PDF is not already on disk, this method attempts to
|
If the patent PDF is not already on disk, this method attempts to
|
||||||
@@ -116,6 +117,7 @@ class CompanyAnalyzer:
|
|||||||
Args:
|
Args:
|
||||||
patent_id: Publication ID of the patent (e.g. "US-11234567-B2")
|
patent_id: Publication ID of the patent (e.g. "US-11234567-B2")
|
||||||
company_name: Name of the company (for context)
|
company_name: Name of the company (for context)
|
||||||
|
model: Optional LLM model override (e.g. 'openai/gpt-4o')
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Analysis of the specific patent's innovation quality
|
Analysis of the specific patent's innovation quality
|
||||||
@@ -151,7 +153,7 @@ class CompanyAnalyzer:
|
|||||||
minimized_content = SERP.minimize_patent_for_llm(sections)
|
minimized_content = SERP.minimize_patent_for_llm(sections)
|
||||||
|
|
||||||
analysis = self.llm_analyzer.analyze_patent_content(
|
analysis = self.llm_analyzer.analyze_patent_content(
|
||||||
patent_content=minimized_content, company_name=company_name
|
patent_content=minimized_content, company_name=company_name, model=model
|
||||||
)
|
)
|
||||||
|
|
||||||
return analysis
|
return analysis
|
||||||
@@ -201,18 +203,19 @@ class CompanyAnalyzer:
|
|||||||
logger.warning("Failed to process %s: %s", patent.patent_id, e)
|
logger.warning("Failed to process %s: %s", patent.patent_id, e)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _analyze_company_safe(self, company_name: str) -> CompanyAnalysisResult:
|
def _analyze_company_safe(self, company_name: str, model: str | None = None) -> CompanyAnalysisResult:
|
||||||
"""Internal wrapper that catches exceptions and returns structured result.
|
"""Internal wrapper that catches exceptions and returns structured result.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
company_name: Name of the company to analyze
|
company_name: Name of the company to analyze
|
||||||
|
model: Optional LLM model override (e.g. 'openai/gpt-4o')
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
CompanyAnalysisResult with success/failure status
|
CompanyAnalysisResult with success/failure status
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Delegate to analyze_company which handles SERP/patent caching
|
# Delegate to analyze_company which handles SERP/patent caching
|
||||||
analysis = self.analyze_company(company_name)
|
analysis = self.analyze_company(company_name, model=model)
|
||||||
|
|
||||||
# Determine patent count from cached SERP query
|
# Determine patent count from cached SERP query
|
||||||
query_hash = hashlib.sha256(company_name.lower().encode()).hexdigest()
|
query_hash = hashlib.sha256(company_name.lower().encode()).hexdigest()
|
||||||
@@ -252,6 +255,7 @@ class CompanyAnalyzer:
|
|||||||
companies: list[str],
|
companies: list[str],
|
||||||
max_workers: int = 3,
|
max_workers: int = 3,
|
||||||
progress_callback: Callable[[str, int, int], None] | None = None,
|
progress_callback: Callable[[str, int, int], None] | None = None,
|
||||||
|
model: str | None = None,
|
||||||
) -> BatchAnalysisResult:
|
) -> BatchAnalysisResult:
|
||||||
"""Analyze multiple companies' patent portfolios in batch.
|
"""Analyze multiple companies' patent portfolios in batch.
|
||||||
|
|
||||||
@@ -262,6 +266,7 @@ class CompanyAnalyzer:
|
|||||||
companies: List of company names to analyze
|
companies: List of company names to analyze
|
||||||
max_workers: Maximum concurrent analyses (default 3 to avoid rate limits)
|
max_workers: Maximum concurrent analyses (default 3 to avoid rate limits)
|
||||||
progress_callback: Optional callback(company_name, completed, total)
|
progress_callback: Optional callback(company_name, completed, total)
|
||||||
|
model: Optional LLM model override (e.g. 'openai/gpt-4o')
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
BatchAnalysisResult containing all individual results and summary stats
|
BatchAnalysisResult containing all individual results and summary stats
|
||||||
@@ -273,7 +278,7 @@ class CompanyAnalyzer:
|
|||||||
|
|
||||||
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
||||||
future_to_company = {
|
future_to_company = {
|
||||||
executor.submit(self._analyze_company_safe, company): company
|
executor.submit(self._analyze_company_safe, company, model): company
|
||||||
for company in companies
|
for company in companies
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-3
@@ -799,6 +799,7 @@ async def health_check():
|
|||||||
)
|
)
|
||||||
async def analyze_company(
|
async def analyze_company(
|
||||||
company_name: str,
|
company_name: str,
|
||||||
|
model: str | None = Query(default=None, description="LLM model to use (e.g. 'openai/gpt-4o'). Defaults to server config."),
|
||||||
_: UserResponse = Depends(get_current_user),
|
_: UserResponse = Depends(get_current_user),
|
||||||
):
|
):
|
||||||
"""Analyze a single company's patent portfolio.
|
"""Analyze a single company's patent portfolio.
|
||||||
@@ -808,6 +809,7 @@ async def analyze_company(
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
company_name: Name of the company to analyze (e.g., "nvidia", "intel")
|
company_name: Name of the company to analyze (e.g., "nvidia", "intel")
|
||||||
|
model: Optional LLM model override
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Analysis results including patent count, AI insights, and success status
|
Analysis results including patent count, AI insights, and success status
|
||||||
@@ -815,7 +817,7 @@ async def analyze_company(
|
|||||||
if not _analyzer:
|
if not _analyzer:
|
||||||
raise HTTPException(status_code=503, detail="Analyzer not initialized")
|
raise HTTPException(status_code=503, detail="Analyzer not initialized")
|
||||||
|
|
||||||
result = _analyzer._analyze_company_safe(company_name)
|
result = _analyzer._analyze_company_safe(company_name, model=model)
|
||||||
return _convert_result(result)
|
return _convert_result(result)
|
||||||
|
|
||||||
|
|
||||||
@@ -877,6 +879,7 @@ async def analyze_companies_batch(
|
|||||||
result = _analyzer.analyze_companies(
|
result = _analyzer.analyze_companies(
|
||||||
companies=request.companies,
|
companies=request.companies,
|
||||||
max_workers=request.max_workers,
|
max_workers=request.max_workers,
|
||||||
|
model=request.model,
|
||||||
)
|
)
|
||||||
return _convert_batch_result(result)
|
return _convert_batch_result(result)
|
||||||
|
|
||||||
@@ -908,7 +911,7 @@ def _job_row_to_status(row: dict) -> JobStatus:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _run_batch_job(job_id: str, companies: list[str], max_workers: int):
|
def _run_batch_job(job_id: str, companies: list[str], max_workers: int, model: str | None = None):
|
||||||
"""Background task for batch analysis."""
|
"""Background task for batch analysis."""
|
||||||
import json as _json
|
import json as _json
|
||||||
global _analyzer
|
global _analyzer
|
||||||
@@ -933,6 +936,7 @@ def _run_batch_job(job_id: str, companies: list[str], max_workers: int):
|
|||||||
companies=companies,
|
companies=companies,
|
||||||
max_workers=max_workers,
|
max_workers=max_workers,
|
||||||
progress_callback=progress_callback,
|
progress_callback=progress_callback,
|
||||||
|
model=model,
|
||||||
)
|
)
|
||||||
batch_response = _convert_batch_result(result)
|
batch_response = _convert_batch_result(result)
|
||||||
db.update_job(
|
db.update_job(
|
||||||
@@ -988,7 +992,7 @@ async def analyze_companies_async(
|
|||||||
job_row = db.create_job(job_id=job_id, total_companies=len(request.companies))
|
job_row = db.create_job(job_id=job_id, total_companies=len(request.companies))
|
||||||
|
|
||||||
background_tasks.add_task(
|
background_tasks.add_task(
|
||||||
_run_batch_job, job_id, request.companies, request.max_workers
|
_run_batch_job, job_id, request.companies, request.max_workers, request.model
|
||||||
)
|
)
|
||||||
|
|
||||||
return _job_row_to_status(job_row)
|
return _job_row_to_status(job_row)
|
||||||
|
|||||||
@@ -89,29 +89,53 @@ export const authApi = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Model types
|
||||||
|
export interface ModelInfo {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
provider: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ModelsResponse {
|
||||||
|
models: ModelInfo[];
|
||||||
|
default: string;
|
||||||
|
}
|
||||||
|
|
||||||
// Analysis API
|
// Analysis API
|
||||||
export const analysisApi = {
|
export const analysisApi = {
|
||||||
analyzeCompany: async (companyName: string): Promise<CompanyAnalysis> => {
|
analyzeCompany: async (companyName: string, model?: string): Promise<CompanyAnalysis> => {
|
||||||
const response = await api.get<CompanyAnalysis>(`/analyze/${encodeURIComponent(companyName)}`);
|
const params = new URLSearchParams();
|
||||||
|
if (model) params.append('model', model);
|
||||||
|
const qs = params.toString();
|
||||||
|
const response = await api.get<CompanyAnalysis>(
|
||||||
|
`/analyze/${encodeURIComponent(companyName)}${qs ? `?${qs}` : ''}`
|
||||||
|
);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
analyzeBatch: async (companies: string[], maxWorkers = 3): Promise<BatchAnalysisResult> => {
|
analyzeBatch: async (companies: string[], maxWorkers = 3, model?: string): Promise<BatchAnalysisResult> => {
|
||||||
const response = await api.post<BatchAnalysisResult>('/analyze/batch', {
|
const response = await api.post<BatchAnalysisResult>('/analyze/batch', {
|
||||||
companies,
|
companies,
|
||||||
max_workers: maxWorkers,
|
max_workers: maxWorkers,
|
||||||
|
...(model ? { model } : {}),
|
||||||
});
|
});
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
analyzeBatchAsync: async (companies: string[], maxWorkers = 3): Promise<JobStatus> => {
|
analyzeBatchAsync: async (companies: string[], maxWorkers = 3, model?: string): Promise<JobStatus> => {
|
||||||
const response = await api.post<JobStatus>('/analyze/batch/async', {
|
const response = await api.post<JobStatus>('/analyze/batch/async', {
|
||||||
companies,
|
companies,
|
||||||
max_workers: maxWorkers,
|
max_workers: maxWorkers,
|
||||||
|
...(model ? { model } : {}),
|
||||||
});
|
});
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
listModels: async (): Promise<ModelsResponse> => {
|
||||||
|
const response = await api.get<ModelsResponse>('/models');
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
|
||||||
getJobStatus: async (jobId: string): Promise<JobStatus> => {
|
getJobStatus: async (jobId: string): Promise<JobStatus> => {
|
||||||
const response = await api.get<JobStatus>(`/jobs/${jobId}`);
|
const response = await api.get<JobStatus>(`/jobs/${jobId}`);
|
||||||
return response.data;
|
return response.data;
|
||||||
|
|||||||
Vendored
+975
@@ -0,0 +1,975 @@
|
|||||||
|
/**
|
||||||
|
* This file was auto-generated by openapi-typescript.
|
||||||
|
* Do not make direct changes to the file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface paths {
|
||||||
|
"/auth/register": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
/**
|
||||||
|
* Register
|
||||||
|
* @description Register a new user.
|
||||||
|
*
|
||||||
|
* The first registered user automatically becomes an admin.
|
||||||
|
*/
|
||||||
|
post: operations["register_auth_register_post"];
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
"/auth/login": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
/**
|
||||||
|
* Login
|
||||||
|
* @description Authenticate user and return JWT tokens.
|
||||||
|
*/
|
||||||
|
post: operations["login_auth_login_post"];
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
"/auth/refresh": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
/**
|
||||||
|
* Refresh Token
|
||||||
|
* @description Refresh access token using refresh token.
|
||||||
|
*/
|
||||||
|
post: operations["refresh_token_auth_refresh_post"];
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
"/auth/me": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Get Me
|
||||||
|
* @description Get current authenticated user.
|
||||||
|
*/
|
||||||
|
get: operations["get_me_auth_me_get"];
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
"/admin/users": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* List Users
|
||||||
|
* @description List all users (admin only).
|
||||||
|
*/
|
||||||
|
get: operations["list_users_admin_users_get"];
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
"/admin/users/{user_id}/role": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
/**
|
||||||
|
* Update User Role
|
||||||
|
* @description Update a user's role (admin only).
|
||||||
|
*/
|
||||||
|
patch: operations["update_user_role_admin_users__user_id__role_patch"];
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
"/admin/users/{user_id}": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
/**
|
||||||
|
* Delete User
|
||||||
|
* @description Delete a user (admin only).
|
||||||
|
*/
|
||||||
|
delete: operations["delete_user_admin_users__user_id__delete"];
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
"/analytics": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Get Analytics
|
||||||
|
* @description Get analytics data (authenticated users only).
|
||||||
|
*/
|
||||||
|
get: operations["get_analytics_analytics_get"];
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
"/health": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Health Check
|
||||||
|
* @description Check API health status.
|
||||||
|
*/
|
||||||
|
get: operations["health_check_health_get"];
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
"/analyze/{company_name}": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Analyze Company
|
||||||
|
* @description Analyze a single company's patent portfolio.
|
||||||
|
*
|
||||||
|
* This endpoint retrieves recent patents for the specified company,
|
||||||
|
* parses them, and uses AI to generate a comprehensive analysis.
|
||||||
|
*
|
||||||
|
* Args:
|
||||||
|
* company_name: Name of the company to analyze (e.g., "nvidia", "intel")
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Analysis results including patent count, AI insights, and success status
|
||||||
|
*/
|
||||||
|
get: operations["analyze_company_analyze__company_name__get"];
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
"/analyze/batch": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
/**
|
||||||
|
* Analyze Companies Batch
|
||||||
|
* @description Analyze multiple companies' patent portfolios.
|
||||||
|
*
|
||||||
|
* Processes companies concurrently for improved performance.
|
||||||
|
* Limited to 20 companies per request.
|
||||||
|
*
|
||||||
|
* Args:
|
||||||
|
* request: List of company names and optional worker count
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Batch results with individual company analyses and summary statistics
|
||||||
|
*/
|
||||||
|
post: operations["analyze_companies_batch_analyze_batch_post"];
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
"/analyze/batch/async": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
/**
|
||||||
|
* Analyze Companies Async
|
||||||
|
* @description Start an asynchronous batch analysis job.
|
||||||
|
*
|
||||||
|
* Returns immediately with a job ID that can be used to poll for status.
|
||||||
|
* Useful for large batch analyses that may take a long time.
|
||||||
|
*
|
||||||
|
* Args:
|
||||||
|
* request: List of company names and optional worker count
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Job status with job_id for polling
|
||||||
|
*/
|
||||||
|
post: operations["analyze_companies_async_analyze_batch_async_post"];
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
"/jobs/{job_id}": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Get Job Status
|
||||||
|
* @description Get the status of a background analysis job.
|
||||||
|
*
|
||||||
|
* Args:
|
||||||
|
* job_id: The job ID returned from the async batch endpoint
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Current job status including progress and results when complete
|
||||||
|
*/
|
||||||
|
get: operations["get_job_status_jobs__job_id__get"];
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
"/jobs": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* List Jobs
|
||||||
|
* @description List all analysis jobs.
|
||||||
|
*
|
||||||
|
* Args:
|
||||||
|
* status: Optional filter by job status
|
||||||
|
* limit: Maximum number of jobs to return (default 10, max 100)
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* List of job statuses
|
||||||
|
*/
|
||||||
|
get: operations["list_jobs_jobs_get"];
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export type webhooks = Record<string, never>;
|
||||||
|
export interface components {
|
||||||
|
schemas: {
|
||||||
|
/**
|
||||||
|
* AnalyticsResponse
|
||||||
|
* @description Analytics response model.
|
||||||
|
*/
|
||||||
|
AnalyticsResponse: {
|
||||||
|
/** Total Messages */
|
||||||
|
total_messages: number;
|
||||||
|
/** By Company */
|
||||||
|
by_company: {
|
||||||
|
[key: string]: unknown;
|
||||||
|
}[];
|
||||||
|
/** By Type */
|
||||||
|
by_type: {
|
||||||
|
[key: string]: unknown;
|
||||||
|
}[];
|
||||||
|
/** Period Days */
|
||||||
|
period_days: number;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* BatchAnalysisRequest
|
||||||
|
* @description Request model for batch company analysis.
|
||||||
|
*/
|
||||||
|
BatchAnalysisRequest: {
|
||||||
|
/**
|
||||||
|
* Companies
|
||||||
|
* @description List of company names to analyze
|
||||||
|
*/
|
||||||
|
companies: string[];
|
||||||
|
/**
|
||||||
|
* Max Workers
|
||||||
|
* @description Max concurrent analyses
|
||||||
|
* @default 3
|
||||||
|
*/
|
||||||
|
max_workers: number;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* BatchAnalysisResponse
|
||||||
|
* @description Response model for batch company analysis.
|
||||||
|
*/
|
||||||
|
BatchAnalysisResponse: {
|
||||||
|
/** Results */
|
||||||
|
results: components["schemas"]["CompanyAnalysisResponse"][];
|
||||||
|
/** Total Companies */
|
||||||
|
total_companies: number;
|
||||||
|
/** Successful */
|
||||||
|
successful: number;
|
||||||
|
/** Failed */
|
||||||
|
failed: number;
|
||||||
|
/**
|
||||||
|
* Timestamp
|
||||||
|
* Format: date-time
|
||||||
|
*/
|
||||||
|
timestamp: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* CompanyAnalysisResponse
|
||||||
|
* @description Response model for single company analysis.
|
||||||
|
*/
|
||||||
|
CompanyAnalysisResponse: {
|
||||||
|
/** Company Name */
|
||||||
|
company_name: string;
|
||||||
|
/** Analysis */
|
||||||
|
analysis: string;
|
||||||
|
/** Patent Count */
|
||||||
|
patent_count: number;
|
||||||
|
/** Success */
|
||||||
|
success: boolean;
|
||||||
|
/** Error */
|
||||||
|
error?: string | null;
|
||||||
|
/**
|
||||||
|
* Timestamp
|
||||||
|
* Format: date-time
|
||||||
|
*/
|
||||||
|
timestamp: string;
|
||||||
|
};
|
||||||
|
/** HTTPValidationError */
|
||||||
|
HTTPValidationError: {
|
||||||
|
/** Detail */
|
||||||
|
detail?: components["schemas"]["ValidationError"][];
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* HealthResponse
|
||||||
|
* @description Health check response.
|
||||||
|
*/
|
||||||
|
HealthResponse: {
|
||||||
|
/** Status */
|
||||||
|
status: string;
|
||||||
|
/** Version */
|
||||||
|
version: string;
|
||||||
|
/**
|
||||||
|
* Timestamp
|
||||||
|
* Format: date-time
|
||||||
|
*/
|
||||||
|
timestamp: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* JobStatus
|
||||||
|
* @description Status of a background analysis job.
|
||||||
|
*/
|
||||||
|
JobStatus: {
|
||||||
|
/** Job Id */
|
||||||
|
job_id: string;
|
||||||
|
/** Status */
|
||||||
|
status: string;
|
||||||
|
/** Progress */
|
||||||
|
progress: number;
|
||||||
|
/** Total Companies */
|
||||||
|
total_companies: number;
|
||||||
|
/** Completed Companies */
|
||||||
|
completed_companies: number;
|
||||||
|
result?: components["schemas"]["BatchAnalysisResponse"] | null;
|
||||||
|
/** Error */
|
||||||
|
error?: string | null;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* LoginRequest
|
||||||
|
* @description User login request.
|
||||||
|
*/
|
||||||
|
LoginRequest: {
|
||||||
|
/**
|
||||||
|
* Email
|
||||||
|
* Format: email
|
||||||
|
*/
|
||||||
|
email: string;
|
||||||
|
/** Password */
|
||||||
|
password: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* RefreshRequest
|
||||||
|
* @description Token refresh request.
|
||||||
|
*/
|
||||||
|
RefreshRequest: {
|
||||||
|
/** Refresh Token */
|
||||||
|
refresh_token: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* RegisterRequest
|
||||||
|
* @description User registration request.
|
||||||
|
*/
|
||||||
|
RegisterRequest: {
|
||||||
|
/**
|
||||||
|
* Email
|
||||||
|
* Format: email
|
||||||
|
*/
|
||||||
|
email: string;
|
||||||
|
/**
|
||||||
|
* Password
|
||||||
|
* @description Password (min 8 characters)
|
||||||
|
*/
|
||||||
|
password: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* TokenResponse
|
||||||
|
* @description Token response model.
|
||||||
|
*/
|
||||||
|
TokenResponse: {
|
||||||
|
/** Access Token */
|
||||||
|
access_token: string;
|
||||||
|
/** Refresh Token */
|
||||||
|
refresh_token: string;
|
||||||
|
/**
|
||||||
|
* Token Type
|
||||||
|
* @default bearer
|
||||||
|
*/
|
||||||
|
token_type: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* UpdateRoleRequest
|
||||||
|
* @description Update user role request.
|
||||||
|
*/
|
||||||
|
UpdateRoleRequest: {
|
||||||
|
/** Role */
|
||||||
|
role: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* UserResponse
|
||||||
|
* @description User response model.
|
||||||
|
*/
|
||||||
|
UserResponse: {
|
||||||
|
/** Id */
|
||||||
|
id: number;
|
||||||
|
/** Email */
|
||||||
|
email: string;
|
||||||
|
/** Role */
|
||||||
|
role: string;
|
||||||
|
/**
|
||||||
|
* Created At
|
||||||
|
* Format: date-time
|
||||||
|
*/
|
||||||
|
created_at: string;
|
||||||
|
};
|
||||||
|
/** ValidationError */
|
||||||
|
ValidationError: {
|
||||||
|
/** Location */
|
||||||
|
loc: (string | number)[];
|
||||||
|
/** Message */
|
||||||
|
msg: string;
|
||||||
|
/** Error Type */
|
||||||
|
type: string;
|
||||||
|
/** Input */
|
||||||
|
input?: unknown;
|
||||||
|
/** Context */
|
||||||
|
ctx?: Record<string, never>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: never;
|
||||||
|
parameters: never;
|
||||||
|
requestBodies: never;
|
||||||
|
headers: never;
|
||||||
|
pathItems: never;
|
||||||
|
}
|
||||||
|
export type $defs = Record<string, never>;
|
||||||
|
export interface operations {
|
||||||
|
register_auth_register_post: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["RegisterRequest"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["UserResponse"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
login_auth_login_post: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["LoginRequest"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["TokenResponse"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
refresh_token_auth_refresh_post: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["RefreshRequest"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["TokenResponse"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
get_me_auth_me_get: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["UserResponse"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
list_users_admin_users_get: {
|
||||||
|
parameters: {
|
||||||
|
query?: {
|
||||||
|
limit?: number;
|
||||||
|
offset?: number;
|
||||||
|
};
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["UserResponse"][];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
update_user_role_admin_users__user_id__role_patch: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path: {
|
||||||
|
user_id: number;
|
||||||
|
};
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["UpdateRoleRequest"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["UserResponse"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
delete_user_admin_users__user_id__delete: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path: {
|
||||||
|
user_id: number;
|
||||||
|
};
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
get_analytics_analytics_get: {
|
||||||
|
parameters: {
|
||||||
|
query?: {
|
||||||
|
days?: number;
|
||||||
|
};
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["AnalyticsResponse"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
health_check_health_get: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HealthResponse"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
analyze_company_analyze__company_name__get: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path: {
|
||||||
|
company_name: string;
|
||||||
|
};
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["CompanyAnalysisResponse"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
analyze_companies_batch_analyze_batch_post: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["BatchAnalysisRequest"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["BatchAnalysisResponse"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
analyze_companies_async_analyze_batch_async_post: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["BatchAnalysisRequest"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["JobStatus"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
get_job_status_jobs__job_id__get: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path: {
|
||||||
|
job_id: string;
|
||||||
|
};
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["JobStatus"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
list_jobs_jobs_get: {
|
||||||
|
parameters: {
|
||||||
|
query?: {
|
||||||
|
/** @description Filter by status: pending, running, completed, failed */
|
||||||
|
status?: string | null;
|
||||||
|
limit?: number;
|
||||||
|
};
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["JobStatus"][];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,15 +1,21 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useMutation } from '@tanstack/react-query';
|
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||||
import { analysisApi, exportApi } from '../api/client';
|
import { analysisApi, exportApi } from '../api/client';
|
||||||
import { Search, CheckCircle, AlertCircle, Clock, FileText, Download } from 'lucide-react';
|
import { Search, CheckCircle, AlertCircle, Clock, FileText, Download, ChevronDown } from 'lucide-react';
|
||||||
import type { CompanyAnalysis } from '../types';
|
import type { CompanyAnalysis } from '../types';
|
||||||
|
|
||||||
export function Analysis() {
|
export function Analysis() {
|
||||||
const [companyName, setCompanyName] = useState('');
|
const [companyName, setCompanyName] = useState('');
|
||||||
|
const [selectedModel, setSelectedModel] = useState('');
|
||||||
const [result, setResult] = useState<CompanyAnalysis | null>(null);
|
const [result, setResult] = useState<CompanyAnalysis | null>(null);
|
||||||
|
|
||||||
|
const modelsQuery = useQuery({
|
||||||
|
queryKey: ['models'],
|
||||||
|
queryFn: () => analysisApi.listModels(),
|
||||||
|
});
|
||||||
|
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationFn: (name: string) => analysisApi.analyzeCompany(name),
|
mutationFn: (name: string) => analysisApi.analyzeCompany(name, selectedModel || undefined),
|
||||||
onSuccess: (data) => setResult(data),
|
onSuccess: (data) => setResult(data),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -33,31 +39,57 @@ export function Analysis() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Search Form */}
|
{/* Search Form */}
|
||||||
<form onSubmit={handleSubmit} className="flex gap-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<div className="flex-1 relative">
|
<div className="flex gap-4">
|
||||||
<Search className="absolute left-4 top-1/2 -translate-y-1/2 text-text-secondary" size={18} />
|
<div className="flex-1 relative">
|
||||||
<input
|
<Search className="absolute left-4 top-1/2 -translate-y-1/2 text-text-secondary" size={18} />
|
||||||
type="text"
|
<input
|
||||||
value={companyName}
|
type="text"
|
||||||
onChange={(e) => setCompanyName(e.target.value)}
|
value={companyName}
|
||||||
placeholder="Enter company name (e.g., nvidia, intel, amd)"
|
onChange={(e) => setCompanyName(e.target.value)}
|
||||||
className="w-full bg-bg-card/80 border border-primary/30 rounded-xl pl-12 pr-4 py-3 text-text-primary placeholder-text-secondary/50 focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20 transition-all"
|
placeholder="Enter company name (e.g., nvidia, intel, amd)"
|
||||||
/>
|
className="w-full bg-bg-card/80 border border-primary/30 rounded-xl pl-12 pr-4 py-3 text-text-primary placeholder-text-secondary/50 focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20 transition-all"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={mutation.isPending || !companyName.trim()}
|
||||||
|
className="bg-gradient-to-r from-primary to-primary-dark text-white font-semibold py-3 px-6 rounded-xl hover:shadow-lg hover:shadow-primary/30 transition-all disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
|
||||||
|
>
|
||||||
|
{mutation.isPending ? (
|
||||||
|
<div className="animate-spin rounded-full h-5 w-5 border-t-2 border-b-2 border-white"></div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Search size={18} />
|
||||||
|
Analyze
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Model Selector */}
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<label className="text-sm font-medium text-text-secondary whitespace-nowrap">
|
||||||
|
LLM Model
|
||||||
|
</label>
|
||||||
|
<div className="relative flex-1 max-w-xs">
|
||||||
|
<select
|
||||||
|
value={selectedModel}
|
||||||
|
onChange={(e) => setSelectedModel(e.target.value)}
|
||||||
|
className="w-full appearance-none bg-bg-card/80 border border-primary/30 rounded-lg pl-3 pr-8 py-2 text-sm text-text-primary focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20 transition-all cursor-pointer"
|
||||||
|
>
|
||||||
|
<option value="">
|
||||||
|
{modelsQuery.data ? `Default (${modelsQuery.data.default})` : 'Default'}
|
||||||
|
</option>
|
||||||
|
{modelsQuery.data?.models.map((m) => (
|
||||||
|
<option key={m.id} value={m.id}>
|
||||||
|
{m.name} ({m.provider})
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<ChevronDown className="absolute right-2 top-1/2 -translate-y-1/2 text-text-secondary pointer-events-none" size={16} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
disabled={mutation.isPending || !companyName.trim()}
|
|
||||||
className="bg-gradient-to-r from-primary to-primary-dark text-white font-semibold py-3 px-6 rounded-xl hover:shadow-lg hover:shadow-primary/30 transition-all disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
|
|
||||||
>
|
|
||||||
{mutation.isPending ? (
|
|
||||||
<div className="animate-spin rounded-full h-5 w-5 border-t-2 border-b-2 border-white"></div>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Search size={18} />
|
|
||||||
Analyze
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{/* Error */}
|
{/* Error */}
|
||||||
|
|||||||
@@ -8,9 +8,15 @@ import type { BatchAnalysisResult } from '../types';
|
|||||||
export function Batch() {
|
export function Batch() {
|
||||||
const [companiesInput, setCompaniesInput] = useState('');
|
const [companiesInput, setCompaniesInput] = useState('');
|
||||||
const [maxWorkers, setMaxWorkers] = useState(3);
|
const [maxWorkers, setMaxWorkers] = useState(3);
|
||||||
|
const [selectedModel, setSelectedModel] = useState('');
|
||||||
const [result, setResult] = useState<BatchAnalysisResult | null>(null);
|
const [result, setResult] = useState<BatchAnalysisResult | null>(null);
|
||||||
const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set());
|
const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set());
|
||||||
|
|
||||||
|
const modelsQuery = useQuery({
|
||||||
|
queryKey: ['models'],
|
||||||
|
queryFn: () => analysisApi.listModels(),
|
||||||
|
});
|
||||||
|
|
||||||
const jobsQuery = useQuery({
|
const jobsQuery = useQuery({
|
||||||
queryKey: ['jobs'],
|
queryKey: ['jobs'],
|
||||||
queryFn: () => analysisApi.listJobs(undefined, 20),
|
queryFn: () => analysisApi.listJobs(undefined, 20),
|
||||||
@@ -18,7 +24,7 @@ export function Batch() {
|
|||||||
|
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationFn: ({ companies, workers }: { companies: string[]; workers: number }) =>
|
mutationFn: ({ companies, workers }: { companies: string[]; workers: number }) =>
|
||||||
analysisApi.analyzeBatch(companies, workers),
|
analysisApi.analyzeBatch(companies, workers, selectedModel || undefined),
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
setResult(data);
|
setResult(data);
|
||||||
jobsQuery.refetch();
|
jobsQuery.refetch();
|
||||||
@@ -93,6 +99,29 @@ export function Batch() {
|
|||||||
<div className="text-center text-text-primary font-semibold">{maxWorkers}</div>
|
<div className="text-center text-text-primary font-semibold">{maxWorkers}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-text-secondary mb-2">
|
||||||
|
LLM Model
|
||||||
|
</label>
|
||||||
|
<div className="relative">
|
||||||
|
<select
|
||||||
|
value={selectedModel}
|
||||||
|
onChange={(e) => setSelectedModel(e.target.value)}
|
||||||
|
className="w-full appearance-none bg-bg-card/80 border border-primary/30 rounded-lg pl-3 pr-8 py-2 text-sm text-text-primary focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20 transition-all cursor-pointer"
|
||||||
|
>
|
||||||
|
<option value="">
|
||||||
|
{modelsQuery.data ? `Default (${modelsQuery.data.default})` : 'Default'}
|
||||||
|
</option>
|
||||||
|
{modelsQuery.data?.models.map((m) => (
|
||||||
|
<option key={m.id} value={m.id}>
|
||||||
|
{m.name} ({m.provider})
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<ChevronDown className="absolute right-2 top-1/2 -translate-y-1/2 text-text-secondary pointer-events-none" size={16} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={mutation.isPending || !companiesInput.trim()}
|
disabled={mutation.isPending || !companiesInput.trim()}
|
||||||
|
|||||||
+28
-42
@@ -1,46 +1,32 @@
|
|||||||
export interface User {
|
/**
|
||||||
id: number;
|
* Application types derived from the auto-generated OpenAPI schema.
|
||||||
email: string;
|
*
|
||||||
role: 'admin' | 'user';
|
* Run `npm run generate:local` (or `npm run generate` with the API running)
|
||||||
created_at: string;
|
* to regenerate `src/api/schema.d.ts` from the backend OpenAPI spec.
|
||||||
}
|
*
|
||||||
|
* These aliases keep the rest of the codebase stable while the source of
|
||||||
|
* truth lives in the generated file.
|
||||||
|
*/
|
||||||
|
|
||||||
export interface TokenResponse {
|
import type { components } from '../api/schema';
|
||||||
access_token: string;
|
|
||||||
refresh_token: string;
|
|
||||||
token_type: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CompanyAnalysis {
|
// Re-export schema types under the names the rest of the app expects.
|
||||||
company_name: string;
|
export type User = components['schemas']['UserResponse'];
|
||||||
analysis: string;
|
export type TokenResponse = components['schemas']['TokenResponse'];
|
||||||
patent_count: number;
|
export type CompanyAnalysis = components['schemas']['CompanyAnalysisResponse'];
|
||||||
success: boolean;
|
export type BatchAnalysisResult = components['schemas']['BatchAnalysisResponse'];
|
||||||
error: string | null;
|
export type JobStatus = components['schemas']['JobStatus'];
|
||||||
timestamp: string;
|
export type Analytics = Omit<components['schemas']['AnalyticsResponse'], 'by_company' | 'by_type'> & {
|
||||||
}
|
|
||||||
|
|
||||||
export interface BatchAnalysisResult {
|
|
||||||
results: CompanyAnalysis[];
|
|
||||||
total_companies: number;
|
|
||||||
successful: number;
|
|
||||||
failed: number;
|
|
||||||
timestamp: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface JobStatus {
|
|
||||||
job_id: string;
|
|
||||||
status: 'pending' | 'running' | 'completed' | 'failed';
|
|
||||||
progress: number;
|
|
||||||
total_companies: number;
|
|
||||||
completed_companies: number;
|
|
||||||
result: BatchAnalysisResult | null;
|
|
||||||
error: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Analytics {
|
|
||||||
total_messages: number;
|
|
||||||
by_company: Array<{ company_name: string; count: number }>;
|
by_company: Array<{ company_name: string; count: number }>;
|
||||||
by_type: Array<{ analysis_type: string; count: number }>;
|
by_type: Array<{ analysis_type: string; count: number }>;
|
||||||
period_days: number;
|
};
|
||||||
}
|
|
||||||
|
// Additional generated types that may be useful elsewhere.
|
||||||
|
export type RegisterRequest = components['schemas']['RegisterRequest'];
|
||||||
|
export type LoginRequest = components['schemas']['LoginRequest'];
|
||||||
|
export type RefreshRequest = components['schemas']['RefreshRequest'];
|
||||||
|
export type UpdateRoleRequest = components['schemas']['UpdateRoleRequest'];
|
||||||
|
export type HealthResponse = components['schemas']['HealthResponse'];
|
||||||
|
export type BatchAnalysisRequest = components['schemas']['BatchAnalysisRequest'];
|
||||||
|
export type ValidationError = components['schemas']['ValidationError'];
|
||||||
|
export type HTTPValidationError = components['schemas']['HTTPValidationError'];
|
||||||
|
|||||||
Reference in New Issue
Block a user