This commit is contained in:
MarkLo 2025-12-20 07:41:05 +08:00
parent f837d4a2d0
commit c4fc8904eb
11 changed files with 385 additions and 225 deletions

View File

@ -104,6 +104,7 @@ async def run_analysis(
deep_think_api_key=request.deep_think_api_key or "",
embedding_base_url=request.embedding_base_url,
embedding_api_key=request.embedding_api_key or "",
embedding_model=request.embedding_model or "all-MiniLM-L6-v2",
alpha_vantage_api_key=request.alpha_vantage_api_key or "",
finmind_api_key=request.finmind_api_key or "",
))
@ -166,7 +167,7 @@ async def cleanup_task(task_id: str):
after the user has saved the results locally or to cloud storage.
This helps keep Redis storage clean and reduces memory usage.
Note: Tasks are also automatically cleaned up 10 minutes after
Note: Tasks are also automatically cleaned up 1 hour after
completion/failure, so calling this endpoint is optional but recommended.
Args:

View File

@ -46,9 +46,13 @@ class AnalysisRequest(BaseModel):
deep_think_api_key: Optional[str] = Field(None, description="API Key for Deep Thinking Model", min_length=0)
embedding_base_url: Optional[str] = Field(
default="https://api.openai.com/v1",
description="Base URL for Embedding Model"
description="Base URL for Embedding Model (only used for OpenAI embeddings)"
)
embedding_api_key: Optional[str] = Field(None, description="API Key for Embedding Model (only used for OpenAI embeddings)", min_length=0)
embedding_model: Optional[str] = Field(
default="all-MiniLM-L6-v2",
description="Embedding model: 'all-MiniLM-L6-v2' (local, no API key), 'text-embedding-3-small' (OpenAI), etc."
)
embedding_api_key: Optional[str] = Field(None, description="API Key for Embedding Model", min_length=0)
alpha_vantage_api_key: Optional[str] = Field(
None,
description="Alpha Vantage API Key (optional, for US stock fundamental data)",

View File

@ -40,7 +40,7 @@ class HybridTaskManager:
self._lock = threading.RLock()
self._cleanup_interval = 3600 # 1 hour
self._task_expiry = 86400 # 24 hours for pending/running tasks
self._completed_task_expiry = 600 # 10 minutes for completed/failed tasks (auto cleanup)
self._completed_task_expiry = 3600 # 1 hour for completed/failed tasks (auto cleanup)
# Check Redis availability on startup
if is_redis_available():

View File

@ -50,6 +50,7 @@ class TradingService:
deep_think_api_key: Optional[str] = None,
embedding_base_url: str = "https://api.openai.com/v1",
embedding_api_key: Optional[str] = None,
embedding_model: str = "all-MiniLM-L6-v2", # Default to local model
alpha_vantage_api_key: Optional[str] = None,
finmind_api_key: Optional[str] = None, # 台灣股市資料 API
market_type: str = "us", # 市場類型us (美股) 或 tw (台股)
@ -132,8 +133,23 @@ class TradingService:
# Note: For non-OpenAI providers, the user MUST provide the specific key if it differs from the shared one.
config["quick_think_api_key"] = quick_think_api_key if quick_think_api_key else openai_api_key
config["deep_think_api_key"] = deep_think_api_key if deep_think_api_key else openai_api_key
config["embedding_base_url"] = normalize_base_url(embedding_base_url)
config["embedding_api_key"] = embedding_api_key if embedding_api_key else openai_api_key
# Embedding configuration: determine provider based on model name
local_embedding_models = ["all-MiniLM-L6-v2", "all-mpnet-base-v2"]
is_local_embedding = embedding_model in local_embedding_models
if is_local_embedding:
# Local embedding: use sentence-transformers (no API key needed)
config["embedding_provider"] = "local"
config["embedding_model"] = embedding_model
logger.info(f"Using local embedding model: {embedding_model}")
else:
# OpenAI embedding: requires API key
config["embedding_provider"] = "openai"
config["embedding_model"] = embedding_model
config["embedding_base_url"] = normalize_base_url(embedding_base_url)
config["embedding_api_key"] = embedding_api_key if embedding_api_key else openai_api_key
logger.info(f"Using OpenAI embedding model: {embedding_model}")
# 根據 market_type 設定資料供應商
if market_type in ["twse", "tpex"]:

View File

@ -38,6 +38,7 @@ stockstats
eodhd
langgraph
chromadb
sentence-transformers
setuptools
backtrader
akshare

View File

@ -52,6 +52,7 @@ const formSchema = z.object({
research_depth: z.number().int().min(1).max(5),
quick_think_llm: z.string().min(1, "請選擇快速思維模型"),
deep_think_llm: z.string().min(1, "請選擇深層思維模型"),
embedding_model: z.string().min(1, "請選擇嵌入式模型"),
// Market type selection: us=美股, twse=上市, tpex=上櫃/興櫃
market_type: z.enum(["us", "twse", "tpex"]),
@ -71,14 +72,14 @@ const formSchema = z.object({
.url("請輸入有效的 URL")
.optional()
.or(z.literal("")),
quick_think_api_key: z.string().min(1, "請輸入快速思維模型 API Key"),
deep_think_api_key: z.string().min(1, "請輸入深層思維模型 API Key"),
quick_think_api_key: z.string().optional().or(z.literal("")),
deep_think_api_key: z.string().optional().or(z.literal("")),
embedding_base_url: z
.string()
.url("請輸入有效的 URL")
.optional()
.or(z.literal("")),
embedding_api_key: z.string().min(1, "請輸入嵌入模型 API Key"),
embedding_api_key: z.string().optional().or(z.literal("")), // 本地模型不需要 API Key
alpha_vantage_api_key: z.string().optional().or(z.literal("")), // 選填
finmind_api_key: z.string().optional().or(z.literal("")), // 選填
});
@ -106,6 +107,7 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
market_type: "us", // 預設美股
quick_think_llm: "gpt-5-mini",
deep_think_llm: "gpt-5-mini",
embedding_model: "all-MiniLM-L6-v2", // 預設使用本地開源模型
custom_quick_think_model: "",
custom_deep_think_model: "",
quick_think_base_url: "https://api.openai.com/v1",
@ -122,9 +124,11 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
// Load API settings from localStorage and update when models change
const quickThinkLlm = form.watch("quick_think_llm");
const deepThinkLlm = form.watch("deep_think_llm");
const embeddingModel = form.watch("embedding_model");
const marketType = form.watch("market_type");
const isQuickThinkCustom = quickThinkLlm === "custom";
const isDeepThinkCustom = deepThinkLlm === "custom";
const isLocalEmbedding = ["all-MiniLM-L6-v2", "all-mpnet-base-v2"].includes(embeddingModel);
useEffect(() => {
// Use async version to get decrypted API keys
@ -169,14 +173,20 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
);
}
form.setValue(
"embedding_base_url",
savedSettings.custom_base_url || "https://api.openai.com/v1"
);
form.setValue(
"embedding_api_key",
savedSettings.custom_api_key || savedSettings.openai_api_key
);
// 本地模型不需要設定 API Key 和 Base URL
if (!isLocalEmbedding) {
form.setValue(
"embedding_base_url",
savedSettings.custom_base_url || "https://api.openai.com/v1"
);
form.setValue(
"embedding_api_key",
savedSettings.custom_api_key || savedSettings.openai_api_key
);
} else {
form.setValue("embedding_base_url", "");
form.setValue("embedding_api_key", "");
}
form.setValue(
"alpha_vantage_api_key",
savedSettings.alpha_vantage_api_key || ""
@ -186,7 +196,7 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
loadSettings();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [quickThinkLlm, deepThinkLlm, isQuickThinkCustom, isDeepThinkCustom]);
}, [quickThinkLlm, deepThinkLlm, embeddingModel, isQuickThinkCustom, isDeepThinkCustom, isLocalEmbedding]);
// 當市場類型改變時,更新預設股票代碼和提示
useEffect(() => {
@ -253,6 +263,17 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
return;
}
// Validate API keys are set (they come from localStorage/settings)
if (!values.quick_think_api_key) {
alert("請先在右上角「設定」中設定您的 API Key。\n\n快速思維模型需要對應的 API Key 才能運作。");
return;
}
if (!values.deep_think_api_key) {
alert("請先在右上角「設定」中設定您的 API Key。\n\n深層思維模型需要對應的 API Key 才能運作。");
return;
}
const request: AnalysisRequest = {
...values,
quick_think_llm: finalQuickThinkLlm,
@ -444,8 +465,8 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
/>
</div>
{/* 第二行:研究深度、快速思維模型、深層思維模型3列) */}
<div className="md:col-span-2 grid grid-cols-1 md:grid-cols-3 gap-6">
{/* 第二行:研究深度、快速思維模型、深層思維模型、嵌入式模型4列) */}
<div className="md:col-span-2 grid grid-cols-1 md:grid-cols-4 gap-6">
<FormField
control={form.control}
name="research_depth"
@ -764,6 +785,50 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
)}
/>
)}
{/* 嵌入式模型 */}
<FormField
control={form.control}
name="embedding_model"
render={({ field }) => (
<FormItem>
<FormLabel></FormLabel>
<Select
onValueChange={field.onChange}
defaultValue={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="選擇嵌入式模型" />
</SelectTrigger>
</FormControl>
<SelectContent>
{/* 本地開源模型 (不需要 API Key) */}
<SelectItem value="all-MiniLM-L6-v2">
🖥 本地: all-MiniLM-L6-v2 ()
</SelectItem>
<SelectItem value="all-mpnet-base-v2">
🖥 本地: all-mpnet-base-v2
</SelectItem>
{/* OpenAI API 模型 (需要 API Key) */}
<SelectItem value="text-embedding-3-small">
OpenAI: text-embedding-3-small
</SelectItem>
<SelectItem value="text-embedding-3-large">
OpenAI: text-embedding-3-large
</SelectItem>
</SelectContent>
</Select>
<FormDescription>
{isLocalEmbedding
? "🆓 本地模型不需 API Key"
: "☁️ 需要 OpenAI API Key"}
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</div>
</div>

View File

@ -40,12 +40,14 @@ import { useAuth } from "@/contexts/auth-context";
import { getCloudSettings, saveCloudSettings, isCloudSyncEnabled } from "@/lib/user-api";
const formSchema = z.object({
// Required
openai_api_key: z.string().min(1, "OpenAI API Key 為必填"),
// All API keys are optional - users only need the ones for their selected models
openai_api_key: z.string().optional().or(z.literal("")),
// Optional
// Stock market data APIs
alpha_vantage_api_key: z.string().optional().or(z.literal("")), // 美股基本面資料
finmind_api_key: z.string().optional().or(z.literal("")), // 台灣股市資料
// LLM Providers
anthropic_api_key: z.string().optional().or(z.literal("")),
google_api_key: z.string().optional().or(z.literal("")),
grok_api_key: z.string().optional().or(z.literal("")),
@ -165,27 +167,11 @@ export function ApiSettingsDialog() {
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
{/* Required Section */}
<div className="space-y-4">
<h3 className="text-lg font-semibold text-primary"></h3>
{/* OpenAI API Key */}
<FormField
control={form.control}
name="openai_api_key"
render={({ field }) => (
<FormItem>
<FormLabel>OpenAI API Key *</FormLabel>
<FormControl>
<Input type="password" placeholder="sk-..." {...field} />
</FormControl>
<FormDescription>
OpenAI GPT-4, GPT-5, o4
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
{/* 注意事項 */}
<div className="space-y-2">
<div className="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-3 text-blue-800 dark:text-blue-300 text-sm">
💡 API使 Claude Claude API
</div>
</div>
{/* Stock Market Data APIs Section */}
@ -239,13 +225,29 @@ export function ApiSettingsDialog() {
/>
</div>
{/* Optional LLM Providers Section */}
{/* LLM Providers Section */}
<div className="space-y-4 border-t pt-4">
<h3 className="text-lg font-semibold text-muted-foreground">
LLM
LLM
</h3>
{/* Anthropic API Key */}
{/* OpenAI API Key */}
<FormField
control={form.control}
name="openai_api_key"
render={({ field }) => (
<FormItem>
<FormLabel>OpenAI API Key</FormLabel>
<FormControl>
<Input type="password" placeholder="sk-..." {...field} />
</FormControl>
<FormDescription>
OpenAI GPT-4, GPT-5, o4 OpenAI
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="anthropic_api_key"

View File

@ -20,6 +20,7 @@ export interface AnalysisRequest {
deep_think_api_key?: string;
embedding_base_url?: string;
embedding_api_key?: string;
embedding_model?: string; // Embedding model: 'all-MiniLM-L6-v2' (local), 'text-embedding-3-small' (OpenAI), etc.
alpha_vantage_api_key?: string;
finmind_api_key?: string; // 台灣股市資料 API
}

View File

@ -64,7 +64,7 @@ importers:
version: 19.2.0
react-day-picker:
specifier: ^9.11.3
version: 9.12.0(react@19.2.0)
version: 9.13.0(react@19.2.0)
react-dom:
specifier: 19.2.0
version: 19.2.0(react@19.2.0)
@ -76,7 +76,7 @@ importers:
version: 10.1.0(@types/react@19.2.7)(react@19.2.0)
recharts:
specifier: ^3.4.1
version: 3.5.1(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react-is@16.13.1)(react@19.2.0)(redux@5.0.1)
version: 3.6.0(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react-is@16.13.1)(react@19.2.0)(redux@5.0.1)
rehype-raw:
specifier: ^7.0.0
version: 7.0.0
@ -95,7 +95,7 @@ importers:
version: 4.1.18
'@types/node':
specifier: ^20
version: 20.19.26
version: 20.19.27
'@types/react':
specifier: ^19
version: 19.2.7
@ -107,13 +107,13 @@ importers:
version: 1.0.0
baseline-browser-mapping:
specifier: ^2.9.2
version: 2.9.6
version: 2.9.11
eslint:
specifier: ^9
version: 9.39.1(jiti@2.6.1)
version: 9.39.2(jiti@2.6.1)
eslint-config-next:
specifier: 16.0.3
version: 16.0.3(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
version: 16.0.3(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
tailwindcss:
specifier: ^4
version: 4.1.18
@ -235,8 +235,8 @@ packages:
resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/js@9.39.1':
resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==}
'@eslint/js@9.39.2':
resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/object-schema@2.1.7':
@ -900,8 +900,8 @@ packages:
'@radix-ui/rect@1.1.1':
resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==}
'@reduxjs/toolkit@2.11.1':
resolution: {integrity: sha512-HjhlEREguAyBTGNzRlGNiDHGQ2EjLSPWwdhhpoEqHYy8hWak3Dp6/fU72OfqVsiMb8S6rbfPsWUF24fxpilrVA==}
'@reduxjs/toolkit@2.11.2':
resolution: {integrity: sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==}
peerDependencies:
react: ^16.9.0 || ^17.0.0 || ^18 || ^19
react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0
@ -914,8 +914,8 @@ packages:
'@rtsao/scc@1.1.0':
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
'@standard-schema/spec@1.0.0':
resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
'@standard-schema/spec@1.1.0':
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
'@standard-schema/utils@0.3.0':
resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==}
@ -1065,8 +1065,8 @@ packages:
'@types/ms@2.1.0':
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
'@types/node@20.19.26':
resolution: {integrity: sha512-0l6cjgF0XnihUpndDhk+nyD3exio3iKaYROSgvh/qSevPXax3L8p5DBRFjbvalnwatGgHEQn2R88y2fA3g4irg==}
'@types/node@20.19.27':
resolution: {integrity: sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==}
'@types/react-dom@19.2.3':
resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==}
@ -1085,63 +1085,63 @@ packages:
'@types/use-sync-external-store@0.0.6':
resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==}
'@typescript-eslint/eslint-plugin@8.49.0':
resolution: {integrity: sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==}
'@typescript-eslint/eslint-plugin@8.50.0':
resolution: {integrity: sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
'@typescript-eslint/parser': ^8.49.0
'@typescript-eslint/parser': ^8.50.0
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/parser@8.49.0':
resolution: {integrity: sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==}
'@typescript-eslint/parser@8.50.0':
resolution: {integrity: sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/project-service@8.49.0':
resolution: {integrity: sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==}
'@typescript-eslint/project-service@8.50.0':
resolution: {integrity: sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/scope-manager@8.49.0':
resolution: {integrity: sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==}
'@typescript-eslint/scope-manager@8.50.0':
resolution: {integrity: sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/tsconfig-utils@8.49.0':
resolution: {integrity: sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==}
'@typescript-eslint/tsconfig-utils@8.50.0':
resolution: {integrity: sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/type-utils@8.49.0':
resolution: {integrity: sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==}
'@typescript-eslint/type-utils@8.50.0':
resolution: {integrity: sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/types@8.49.0':
resolution: {integrity: sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==}
'@typescript-eslint/types@8.50.0':
resolution: {integrity: sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/typescript-estree@8.49.0':
resolution: {integrity: sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==}
'@typescript-eslint/typescript-estree@8.50.0':
resolution: {integrity: sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/utils@8.49.0':
resolution: {integrity: sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==}
'@typescript-eslint/utils@8.50.0':
resolution: {integrity: sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/visitor-keys@8.49.0':
resolution: {integrity: sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==}
'@typescript-eslint/visitor-keys@8.50.0':
resolution: {integrity: sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@ungap/structured-clone@1.3.0':
@ -1336,8 +1336,8 @@ packages:
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
baseline-browser-mapping@2.9.6:
resolution: {integrity: sha512-v9BVVpOTLB59C9E7aSnmIF8h7qRsFpx+A2nugVMTszEOMcfjlZMsXRm4LF23I3Z9AJxc8ANpIvzbzONoX9VJlg==}
baseline-browser-mapping@2.9.11:
resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==}
hasBin: true
brace-expansion@1.1.12:
@ -1371,8 +1371,8 @@ packages:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
caniuse-lite@1.0.30001760:
resolution: {integrity: sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==}
caniuse-lite@1.0.30001761:
resolution: {integrity: sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==}
ccount@2.0.1:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
@ -1572,8 +1572,8 @@ packages:
resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
engines: {node: '>=0.12'}
es-abstract@1.24.0:
resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==}
es-abstract@1.24.1:
resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==}
engines: {node: '>= 0.4'}
es-define-property@1.0.1:
@ -1584,8 +1584,8 @@ packages:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
es-iterator-helpers@1.2.1:
resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==}
es-iterator-helpers@1.2.2:
resolution: {integrity: sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==}
engines: {node: '>= 0.4'}
es-object-atoms@1.1.1:
@ -1604,8 +1604,8 @@ packages:
resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
engines: {node: '>= 0.4'}
es-toolkit@1.42.0:
resolution: {integrity: sha512-SLHIyY7VfDJBM8clz4+T2oquwTQxEzu263AyhVK4jREOAwJ+8eebaa4wM3nlvnAqhDrMm2EsA6hWHaQsMPQ1nA==}
es-toolkit@1.43.0:
resolution: {integrity: sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA==}
escalade@3.2.0:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
@ -1705,8 +1705,8 @@ packages:
resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
eslint@9.39.1:
resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==}
eslint@9.39.2:
resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
hasBin: true
peerDependencies:
@ -2558,8 +2558,8 @@ packages:
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
react-day-picker@9.12.0:
resolution: {integrity: sha512-t8OvG/Zrciso5CQJu5b1A7yzEmebvST+S3pOVQJWxwjjVngyG/CA2htN/D15dLI4uTEuLLkbZyS4YYt480FAtA==}
react-day-picker@9.13.0:
resolution: {integrity: sha512-euzj5Hlq+lOHqI53NiuNhCP8HWgsPf/bBAVijR50hNaY1XwjKjShAnIe8jm8RD2W9IJUvihDIZ+KrmqfFzNhFQ==}
engines: {node: '>=18'}
peerDependencies:
react: '>=16.8.0'
@ -2630,8 +2630,8 @@ packages:
resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==}
engines: {node: '>=0.10.0'}
recharts@3.5.1:
resolution: {integrity: sha512-+v+HJojK7gnEgG6h+b2u7k8HH7FhyFUzAc4+cPrsjL4Otdgqr/ecXzAnHciqlzV1ko064eNcsdzrYOM78kankA==}
recharts@3.6.0:
resolution: {integrity: sha512-L5bjxvQRAe26RlToBAziKUB7whaGKEwD3znoM6fz3DrTowCIC/FnJYnuq1GEzB8Zv2kdTfaxQfi5GoH0tBinyg==}
engines: {node: '>=18'}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
@ -2896,8 +2896,8 @@ packages:
resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
engines: {node: '>= 0.4'}
typescript-eslint@8.49.0:
resolution: {integrity: sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg==}
typescript-eslint@8.50.0:
resolution: {integrity: sha512-Q1/6yNUmCpH94fbgMUMg2/BSAr/6U7GBk61kZTv1/asghQOWOjTlp9K8mixS5NcJmm2creY+UFfGeW/+OcA64A==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
@ -2936,8 +2936,8 @@ packages:
unrs-resolver@1.11.1:
resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==}
update-browserslist-db@1.2.2:
resolution: {integrity: sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==}
update-browserslist-db@1.2.3:
resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
hasBin: true
peerDependencies:
browserslist: '>= 4.21.0'
@ -3151,9 +3151,9 @@ snapshots:
tslib: 2.8.1
optional: true
'@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@2.6.1))':
'@eslint-community/eslint-utils@4.9.0(eslint@9.39.2(jiti@2.6.1))':
dependencies:
eslint: 9.39.1(jiti@2.6.1)
eslint: 9.39.2(jiti@2.6.1)
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.2': {}
@ -3188,7 +3188,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@eslint/js@9.39.1': {}
'@eslint/js@9.39.2': {}
'@eslint/object-schema@2.1.7': {}
@ -3785,9 +3785,9 @@ snapshots:
'@radix-ui/rect@1.1.1': {}
'@reduxjs/toolkit@2.11.1(react-redux@9.2.0(@types/react@19.2.7)(react@19.2.0)(redux@5.0.1))(react@19.2.0)':
'@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.2.7)(react@19.2.0)(redux@5.0.1))(react@19.2.0)':
dependencies:
'@standard-schema/spec': 1.0.0
'@standard-schema/spec': 1.1.0
'@standard-schema/utils': 0.3.0
immer: 11.0.1
redux: 5.0.1
@ -3799,7 +3799,7 @@ snapshots:
'@rtsao/scc@1.1.0': {}
'@standard-schema/spec@1.0.0': {}
'@standard-schema/spec@1.1.0': {}
'@standard-schema/utils@0.3.0': {}
@ -3929,7 +3929,7 @@ snapshots:
'@types/ms@2.1.0': {}
'@types/node@20.19.26':
'@types/node@20.19.27':
dependencies:
undici-types: 6.21.0
@ -3947,15 +3947,15 @@ snapshots:
'@types/use-sync-external-store@0.0.6': {}
'@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
'@typescript-eslint/eslint-plugin@8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
'@typescript-eslint/parser': 8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.49.0
'@typescript-eslint/type-utils': 8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.49.0
eslint: 9.39.1(jiti@2.6.1)
'@typescript-eslint/parser': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.50.0
'@typescript-eslint/type-utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.50.0
eslint: 9.39.2(jiti@2.6.1)
ignore: 7.0.5
natural-compare: 1.4.0
ts-api-utils: 2.1.0(typescript@5.9.3)
@ -3963,56 +3963,56 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
'@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@typescript-eslint/scope-manager': 8.49.0
'@typescript-eslint/types': 8.49.0
'@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.49.0
'@typescript-eslint/scope-manager': 8.50.0
'@typescript-eslint/types': 8.50.0
'@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.50.0
debug: 4.4.3
eslint: 9.39.1(jiti@2.6.1)
eslint: 9.39.2(jiti@2.6.1)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/project-service@8.49.0(typescript@5.9.3)':
'@typescript-eslint/project-service@8.50.0(typescript@5.9.3)':
dependencies:
'@typescript-eslint/tsconfig-utils': 8.49.0(typescript@5.9.3)
'@typescript-eslint/types': 8.49.0
'@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3)
'@typescript-eslint/types': 8.50.0
debug: 4.4.3
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/scope-manager@8.49.0':
'@typescript-eslint/scope-manager@8.50.0':
dependencies:
'@typescript-eslint/types': 8.49.0
'@typescript-eslint/visitor-keys': 8.49.0
'@typescript-eslint/types': 8.50.0
'@typescript-eslint/visitor-keys': 8.50.0
'@typescript-eslint/tsconfig-utils@8.49.0(typescript@5.9.3)':
'@typescript-eslint/tsconfig-utils@8.50.0(typescript@5.9.3)':
dependencies:
typescript: 5.9.3
'@typescript-eslint/type-utils@8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
'@typescript-eslint/type-utils@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@typescript-eslint/types': 8.49.0
'@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/types': 8.50.0
'@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
debug: 4.4.3
eslint: 9.39.1(jiti@2.6.1)
eslint: 9.39.2(jiti@2.6.1)
ts-api-utils: 2.1.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/types@8.49.0': {}
'@typescript-eslint/types@8.50.0': {}
'@typescript-eslint/typescript-estree@8.49.0(typescript@5.9.3)':
'@typescript-eslint/typescript-estree@8.50.0(typescript@5.9.3)':
dependencies:
'@typescript-eslint/project-service': 8.49.0(typescript@5.9.3)
'@typescript-eslint/tsconfig-utils': 8.49.0(typescript@5.9.3)
'@typescript-eslint/types': 8.49.0
'@typescript-eslint/visitor-keys': 8.49.0
'@typescript-eslint/project-service': 8.50.0(typescript@5.9.3)
'@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3)
'@typescript-eslint/types': 8.50.0
'@typescript-eslint/visitor-keys': 8.50.0
debug: 4.4.3
minimatch: 9.0.5
semver: 7.7.3
@ -4022,20 +4022,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
'@typescript-eslint/utils@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1))
'@typescript-eslint/scope-manager': 8.49.0
'@typescript-eslint/types': 8.49.0
'@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
eslint: 9.39.1(jiti@2.6.1)
'@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1))
'@typescript-eslint/scope-manager': 8.50.0
'@typescript-eslint/types': 8.50.0
'@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3)
eslint: 9.39.2(jiti@2.6.1)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/visitor-keys@8.49.0':
'@typescript-eslint/visitor-keys@8.50.0':
dependencies:
'@typescript-eslint/types': 8.49.0
'@typescript-eslint/types': 8.50.0
eslint-visitor-keys: 4.2.1
'@ungap/structured-clone@1.3.0': {}
@ -4134,7 +4134,7 @@ snapshots:
call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
es-abstract: 1.24.0
es-abstract: 1.24.1
es-object-atoms: 1.1.1
get-intrinsic: 1.3.0
is-string: 1.1.1
@ -4144,7 +4144,7 @@ snapshots:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
es-abstract: 1.24.0
es-abstract: 1.24.1
es-errors: 1.3.0
es-object-atoms: 1.1.1
es-shim-unscopables: 1.1.0
@ -4154,7 +4154,7 @@ snapshots:
call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
es-abstract: 1.24.0
es-abstract: 1.24.1
es-errors: 1.3.0
es-object-atoms: 1.1.1
es-shim-unscopables: 1.1.0
@ -4163,21 +4163,21 @@ snapshots:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
es-abstract: 1.24.0
es-abstract: 1.24.1
es-shim-unscopables: 1.1.0
array.prototype.flatmap@1.3.3:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
es-abstract: 1.24.0
es-abstract: 1.24.1
es-shim-unscopables: 1.1.0
array.prototype.tosorted@1.1.4:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
es-abstract: 1.24.0
es-abstract: 1.24.1
es-errors: 1.3.0
es-shim-unscopables: 1.1.0
@ -4186,7 +4186,7 @@ snapshots:
array-buffer-byte-length: 1.0.2
call-bind: 1.0.8
define-properties: 1.2.1
es-abstract: 1.24.0
es-abstract: 1.24.1
es-errors: 1.3.0
get-intrinsic: 1.3.0
is-array-buffer: 3.0.5
@ -4221,7 +4221,7 @@ snapshots:
balanced-match@1.0.2: {}
baseline-browser-mapping@2.9.6: {}
baseline-browser-mapping@2.9.11: {}
brace-expansion@1.1.12:
dependencies:
@ -4238,11 +4238,11 @@ snapshots:
browserslist@4.28.1:
dependencies:
baseline-browser-mapping: 2.9.6
caniuse-lite: 1.0.30001760
baseline-browser-mapping: 2.9.11
caniuse-lite: 1.0.30001761
electron-to-chromium: 1.5.267
node-releases: 2.0.27
update-browserslist-db: 1.2.2(browserslist@4.28.1)
update-browserslist-db: 1.2.3(browserslist@4.28.1)
call-bind-apply-helpers@1.0.2:
dependencies:
@ -4263,7 +4263,7 @@ snapshots:
callsites@3.1.0: {}
caniuse-lite@1.0.30001760: {}
caniuse-lite@1.0.30001761: {}
ccount@2.0.1: {}
@ -4437,7 +4437,7 @@ snapshots:
entities@6.0.1: {}
es-abstract@1.24.0:
es-abstract@1.24.1:
dependencies:
array-buffer-byte-length: 1.0.2
arraybuffer.prototype.slice: 1.0.4
@ -4498,12 +4498,12 @@ snapshots:
es-errors@1.3.0: {}
es-iterator-helpers@1.2.1:
es-iterator-helpers@1.2.2:
dependencies:
call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
es-abstract: 1.24.0
es-abstract: 1.24.1
es-errors: 1.3.0
es-set-tostringtag: 2.1.0
function-bind: 1.1.2
@ -4538,7 +4538,7 @@ snapshots:
is-date-object: 1.1.0
is-symbol: 1.1.1
es-toolkit@1.42.0: {}
es-toolkit@1.43.0: {}
escalade@3.2.0: {}
@ -4546,18 +4546,18 @@ snapshots:
escape-string-regexp@5.0.0: {}
eslint-config-next@16.0.3(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3):
eslint-config-next@16.0.3(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3):
dependencies:
'@next/eslint-plugin-next': 16.0.3
eslint: 9.39.1(jiti@2.6.1)
eslint: 9.39.2(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.1(jiti@2.6.1))
eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@2.6.1))
eslint-plugin-react-hooks: 7.0.1(eslint@9.39.1(jiti@2.6.1))
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-react-hooks: 7.0.1(eslint@9.39.2(jiti@2.6.1))
globals: 16.4.0
typescript-eslint: 8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
typescript-eslint: 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
optionalDependencies:
typescript: 5.9.3
transitivePeerDependencies:
@ -4574,33 +4574,33 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)):
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)):
dependencies:
'@nolyfill/is-core-module': 1.0.39
debug: 4.4.3
eslint: 9.39.1(jiti@2.6.1)
eslint: 9.39.2(jiti@2.6.1)
get-tsconfig: 4.13.0
is-bun-module: 2.0.0
stable-hash: 0.0.5
tinyglobby: 0.2.15
unrs-resolver: 1.11.1
optionalDependencies:
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)):
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
eslint: 9.39.1(jiti@2.6.1)
'@typescript-eslint/parser': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
eslint: 9.39.2(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1))
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1))
transitivePeerDependencies:
- supports-color
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)):
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.9
@ -4609,9 +4609,9 @@ snapshots:
array.prototype.flatmap: 1.3.3
debug: 3.2.7
doctrine: 2.1.0
eslint: 9.39.1(jiti@2.6.1)
eslint: 9.39.2(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1))
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@ -4623,13 +4623,13 @@ snapshots:
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
optionalDependencies:
'@typescript-eslint/parser': 8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/parser': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.1(jiti@2.6.1)):
eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.2(jiti@2.6.1)):
dependencies:
aria-query: 5.3.2
array-includes: 3.1.9
@ -4639,7 +4639,7 @@ snapshots:
axobject-query: 4.1.0
damerau-levenshtein: 1.0.8
emoji-regex: 9.2.2
eslint: 9.39.1(jiti@2.6.1)
eslint: 9.39.2(jiti@2.6.1)
hasown: 2.0.2
jsx-ast-utils: 3.3.5
language-tags: 1.0.9
@ -4648,26 +4648,26 @@ snapshots:
safe-regex-test: 1.1.0
string.prototype.includes: 2.0.1
eslint-plugin-react-hooks@7.0.1(eslint@9.39.1(jiti@2.6.1)):
eslint-plugin-react-hooks@7.0.1(eslint@9.39.2(jiti@2.6.1)):
dependencies:
'@babel/core': 7.28.5
'@babel/parser': 7.28.5
eslint: 9.39.1(jiti@2.6.1)
eslint: 9.39.2(jiti@2.6.1)
hermes-parser: 0.25.1
zod: 3.25.76
zod-validation-error: 4.0.2(zod@3.25.76)
transitivePeerDependencies:
- supports-color
eslint-plugin-react@7.37.5(eslint@9.39.1(jiti@2.6.1)):
eslint-plugin-react@7.37.5(eslint@9.39.2(jiti@2.6.1)):
dependencies:
array-includes: 3.1.9
array.prototype.findlast: 1.2.5
array.prototype.flatmap: 1.3.3
array.prototype.tosorted: 1.1.4
doctrine: 2.1.0
es-iterator-helpers: 1.2.1
eslint: 9.39.1(jiti@2.6.1)
es-iterator-helpers: 1.2.2
eslint: 9.39.2(jiti@2.6.1)
estraverse: 5.3.0
hasown: 2.0.2
jsx-ast-utils: 3.3.5
@ -4690,15 +4690,15 @@ snapshots:
eslint-visitor-keys@4.2.1: {}
eslint@9.39.1(jiti@2.6.1):
eslint@9.39.2(jiti@2.6.1):
dependencies:
'@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1))
'@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1))
'@eslint-community/regexpp': 4.12.2
'@eslint/config-array': 0.21.1
'@eslint/config-helpers': 0.4.2
'@eslint/core': 0.17.0
'@eslint/eslintrc': 3.3.3
'@eslint/js': 9.39.1
'@eslint/js': 9.39.2
'@eslint/plugin-kit': 0.4.1
'@humanfs/node': 0.16.7
'@humanwhocodes/module-importer': 1.0.1
@ -5652,7 +5652,7 @@ snapshots:
dependencies:
'@next/env': 16.0.10
'@swc/helpers': 0.5.15
caniuse-lite: 1.0.30001760
caniuse-lite: 1.0.30001761
postcss: 8.4.31
react: 19.2.0
react-dom: 19.2.0(react@19.2.0)
@ -5700,14 +5700,14 @@ snapshots:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
es-abstract: 1.24.0
es-abstract: 1.24.1
es-object-atoms: 1.1.1
object.groupby@1.0.3:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
es-abstract: 1.24.0
es-abstract: 1.24.1
object.values@1.2.1:
dependencies:
@ -5799,7 +5799,7 @@ snapshots:
queue-microtask@1.2.3: {}
react-day-picker@9.12.0(react@19.2.0):
react-day-picker@9.13.0(react@19.2.0):
dependencies:
'@date-fns/tz': 1.4.1
date-fns: 4.1.0
@ -5873,12 +5873,12 @@ snapshots:
react@19.2.0: {}
recharts@3.5.1(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react-is@16.13.1)(react@19.2.0)(redux@5.0.1):
recharts@3.6.0(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react-is@16.13.1)(react@19.2.0)(redux@5.0.1):
dependencies:
'@reduxjs/toolkit': 2.11.1(react-redux@9.2.0(@types/react@19.2.7)(react@19.2.0)(redux@5.0.1))(react@19.2.0)
'@reduxjs/toolkit': 2.11.2(react-redux@9.2.0(@types/react@19.2.7)(react@19.2.0)(redux@5.0.1))(react@19.2.0)
clsx: 2.1.1
decimal.js-light: 2.5.1
es-toolkit: 1.42.0
es-toolkit: 1.43.0
eventemitter3: 5.0.1
immer: 10.2.0
react: 19.2.0
@ -5903,7 +5903,7 @@ snapshots:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
es-abstract: 1.24.0
es-abstract: 1.24.1
es-errors: 1.3.0
es-object-atoms: 1.1.1
get-intrinsic: 1.3.0
@ -6111,14 +6111,14 @@ snapshots:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
es-abstract: 1.24.0
es-abstract: 1.24.1
string.prototype.matchall@4.0.12:
dependencies:
call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
es-abstract: 1.24.0
es-abstract: 1.24.1
es-errors: 1.3.0
es-object-atoms: 1.1.1
get-intrinsic: 1.3.0
@ -6132,7 +6132,7 @@ snapshots:
string.prototype.repeat@1.0.0:
dependencies:
define-properties: 1.2.1
es-abstract: 1.24.0
es-abstract: 1.24.1
string.prototype.trim@1.2.10:
dependencies:
@ -6140,7 +6140,7 @@ snapshots:
call-bound: 1.0.4
define-data-property: 1.1.4
define-properties: 1.2.1
es-abstract: 1.24.0
es-abstract: 1.24.1
es-object-atoms: 1.1.1
has-property-descriptors: 1.0.2
@ -6260,13 +6260,13 @@ snapshots:
possible-typed-array-names: 1.1.0
reflect.getprototypeof: 1.0.10
typescript-eslint@8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3):
typescript-eslint@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3):
dependencies:
'@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/parser': 8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
eslint: 9.39.1(jiti@2.6.1)
'@typescript-eslint/eslint-plugin': 8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/parser': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
eslint: 9.39.2(jiti@2.6.1)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@ -6339,7 +6339,7 @@ snapshots:
'@unrs/resolver-binding-win32-ia32-msvc': 1.11.1
'@unrs/resolver-binding-win32-x64-msvc': 1.11.1
update-browserslist-db@1.2.2(browserslist@4.28.1):
update-browserslist-db@1.2.3(browserslist@4.28.1):
dependencies:
browserslist: 4.28.1
escalade: 3.2.0

View File

@ -10,6 +10,7 @@ stockstats
eodhd
langgraph
chromadb
sentence-transformers
setuptools
backtrader
akshare

View File

@ -1,12 +1,59 @@
import os
import chromadb
from chromadb.config import Settings
from openai import OpenAI
class FinancialSituationMemory:
def __init__(self, name, config):
self.embedding = "text-embedding-3-small"
"""
Initialize the memory with configurable embedding provider.
Config options:
embedding_provider: "local" (default) or "openai"
embedding_model: Model name for the embedding provider
- For local: "all-MiniLM-L6-v2" (default), "all-mpnet-base-v2", etc.
- For OpenAI: "text-embedding-3-small" (default), "text-embedding-3-large", etc.
embedding_base_url: Base URL for OpenAI-compatible API (only used when provider is "openai")
embedding_api_key: API key for OpenAI (only used when provider is "openai")
"""
self.provider = config.get("embedding_provider", "local").lower()
if self.provider == "local":
self._init_local_embedding(config)
elif self.provider == "openai":
self._init_openai_embedding(config)
else:
raise ValueError(f"Unsupported embedding provider: {self.provider}. Use 'local' or 'openai'.")
self.chroma_client = chromadb.Client(Settings(allow_reset=True))
self.situation_collection = self.chroma_client.get_or_create_collection(name=name)
def _init_local_embedding(self, config):
"""Initialize local embedding using sentence-transformers."""
try:
from sentence_transformers import SentenceTransformer
except ImportError:
raise ImportError(
"sentence-transformers is required for local embeddings. "
"Install it with: pip install sentence-transformers"
)
# Default to all-MiniLM-L6-v2 - a lightweight and efficient model
model_name = config.get("embedding_model", "all-MiniLM-L6-v2")
import logging
logging.info(f"Loading local embedding model: {model_name}")
self.model = SentenceTransformer(model_name)
self.embedding_dim = self.model.get_sentence_embedding_dimension()
logging.info(f"Local embedding model loaded. Dimension: {self.embedding_dim}")
def _init_openai_embedding(self, config):
"""Initialize OpenAI embedding client."""
from openai import OpenAI
self.embedding_model = config.get("embedding_model", "text-embedding-3-small")
# Get embedding configuration from config, with fallbacks
embedding_base_url = config.get("embedding_base_url", "https://api.openai.com/v1")
@ -23,23 +70,37 @@ class FinancialSituationMemory:
import logging
logging.warning("Using LLM API key for embeddings. Consider setting embedding_api_key or OPENAI_API_KEY.")
if not embedding_api_key:
raise ValueError(
"OpenAI API key is required for OpenAI embeddings. "
"Set 'embedding_api_key' in config or OPENAI_API_KEY environment variable, "
"or use 'embedding_provider': 'local' for local embeddings without API key."
)
# Use configured endpoint for embeddings
self.client = OpenAI(base_url=embedding_base_url, api_key=embedding_api_key)
self.chroma_client = chromadb.Client(Settings(allow_reset=True))
self.situation_collection = self.chroma_client.get_or_create_collection(name=name)
def get_embedding(self, text):
"""Get OpenAI embedding for a text"""
"""Get embedding for a text using the configured provider."""
# Truncate text to avoid exceeding embedding model's token limit
# text-embedding-3-small has 8192 token limit
# For mixed Chinese/English text, estimate ~1.5-2 tokens per character
# Target: ~4000 characters to stay well under 8192 tokens
max_chars = 4000
if len(text) > max_chars:
text = text[:max_chars]
if self.provider == "local":
return self._get_local_embedding(text)
else:
return self._get_openai_embedding(text)
def _get_local_embedding(self, text):
"""Get embedding using local sentence-transformers model."""
embedding = self.model.encode(text, normalize_embeddings=True)
return embedding.tolist()
def _get_openai_embedding(self, text):
"""Get embedding using OpenAI API."""
response = self.client.embeddings.create(
model=self.embedding, input=text
model=self.embedding_model, input=text
)
return response.data[0].embedding
@ -67,7 +128,7 @@ class FinancialSituationMemory:
)
def get_memories(self, current_situation, n_matches=1):
"""Find matching recommendations using OpenAI embeddings"""
"""Find matching recommendations using embeddings"""
query_embedding = self.get_embedding(current_situation)
results = self.situation_collection.query(
@ -90,8 +151,13 @@ class FinancialSituationMemory:
if __name__ == "__main__":
# Example usage
matcher = FinancialSituationMemory()
# Example usage with local embedding (no API key required!)
config = {
"embedding_provider": "local", # Use local model, no API key needed
"embedding_model": "all-MiniLM-L6-v2", # Lightweight and efficient
}
matcher = FinancialSituationMemory("test_memory", config)
# Example data
example_data = [
@ -114,7 +180,9 @@ if __name__ == "__main__":
]
# Add the example situations and recommendations
print("Adding example situations...")
matcher.add_situations(example_data)
print("Done!")
# Example query
current_situation = """
@ -123,6 +191,7 @@ if __name__ == "__main__":
"""
try:
print("\nSearching for recommendations...")
recommendations = matcher.get_memories(current_situation, n_matches=2)
for i, rec in enumerate(recommendations, 1):
@ -132,4 +201,4 @@ if __name__ == "__main__":
print(f"Recommendation: {rec['recommendation']}")
except Exception as e:
print(f"Error during recommendation: {str(e)}")
print(f"Error during recommendation: {str(e)}")