feat(analyst): Add analyze endpoint and chart specification validation

- Introduced a new `/analyze` endpoint in the analyst API to process analysis requests, allowing users to receive textual interpretations and chart specifications based on provided prompts.
- Enhanced `analyst_schemas.py` with `AnalyzeRequest` and `ChartSpec` models to structure analysis requests and validate chart specifications.
- Implemented chart validation logic in a new `analyst_chart.py` service, ensuring that chart types and fields are correctly specified and conform to allowed values.
- Updated `AnalystAgent` to handle analysis requests, integrating the new logic for generating responses based on user prompts and data availability.
- Added unit tests to verify the functionality of the new endpoint and validation mechanisms, ensuring robustness and reliability.

This update significantly enhances the analytical capabilities of the application, providing users with improved tools for data interpretation and visualization.
This commit is contained in:
2026-09-12 12:33:37 +08:00
parent d4a2461ba4
commit a0f550646e
155 changed files with 43199 additions and 5211 deletions
+50
View File
@@ -0,0 +1,50 @@
const STAR_KEY = 'jinrong.analyst.starredQuestions'
const RECENT_KEY = 'jinrong.analyst.recentQuestions'
const MAX_RECENT = 8
export type RecentQuestion = { question: string; at: number }
function readJson<T>(key: string, fallback: T): T {
try {
const raw = localStorage.getItem(key)
if (!raw) return fallback
return JSON.parse(raw) as T
} catch {
return fallback
}
}
export function loadStarredQuestions(): string[] {
const list = readJson<string[]>(STAR_KEY, [])
return Array.isArray(list) ? list : []
}
export function saveStarredQuestions(questions: string[]): void {
localStorage.setItem(STAR_KEY, JSON.stringify(questions.slice(0, 30)))
}
export function toggleStarredQuestion(question: string): string[] {
const q = question.trim()
if (!q) return loadStarredQuestions()
const set = new Set(loadStarredQuestions())
if (set.has(q)) set.delete(q)
else set.add(q)
const next = [...set]
saveStarredQuestions(next)
return next
}
export function loadRecentQuestions(): RecentQuestion[] {
const list = readJson<RecentQuestion[]>(RECENT_KEY, [])
return Array.isArray(list) ? list : []
}
export function pushRecentQuestion(question: string): RecentQuestion[] {
const q = question.trim()
if (!q) return loadRecentQuestions()
const now = Date.now()
const rest = loadRecentQuestions().filter((r) => r.question !== q)
const next = [{ question: q, at: now }, ...rest].slice(0, MAX_RECENT)
localStorage.setItem(RECENT_KEY, JSON.stringify(next))
return next
}