Add DeepSeek/xAI/OpenAI-compatible sentiment providers; custom dark dropdown
Providers (admin-switchable, no redeploy): - DeepSeek and any OpenAI-compatible endpoint (OpenRouter, Together, Groq, local Ollama) via a generic Chat Completions adapter + base_url - xAI Grok with Live Search (search_parameters web+X, citations) — grounded tier alongside OpenAI and Gemini - DeepSeek / generic compatible endpoints are ungrounded (no web search); UI shows an amber warning and labels each provider's grounding - Optional env fallbacks DEEPSEEK_API_KEY / XAI_API_KEY UI: replace native <select> (unstyleable white popup on Windows) with a custom dark Dropdown component everywhere — sentiment provider, scanner filters, market sort, indicators, admin universe, user role. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,8 @@ import { useToast } from '../ui/Toast';
|
||||
import { Button } from '../ui/Button';
|
||||
import { Callout } from '../ui/Callout';
|
||||
import { Disclosure } from '../ui/Disclosure';
|
||||
import { Field, Input, Select } from '../ui/Field';
|
||||
import { Field, Input } from '../ui/Field';
|
||||
import { Dropdown } from '../ui/Dropdown';
|
||||
import { triggerJob } from '../../api/admin';
|
||||
import type { TradeSetup } from '../../lib/types';
|
||||
import { RECOMMENDATION_ACTION_GLOSSARY, RECOMMENDATION_ACTION_LABELS } from '../../lib/recommendation';
|
||||
@@ -178,15 +179,17 @@ export function SetupsPanel() {
|
||||
</div>
|
||||
</Field>
|
||||
<Field label="Direction" htmlFor="direction">
|
||||
<Select
|
||||
<Dropdown
|
||||
id="direction"
|
||||
value={directionFilter}
|
||||
onChange={(e) => setDirectionFilter(e.target.value as DirectionFilter)}
|
||||
>
|
||||
<option value="both">Both</option>
|
||||
<option value="long">Long</option>
|
||||
<option value="short">Short</option>
|
||||
</Select>
|
||||
onChange={(v) => setDirectionFilter(v as DirectionFilter)}
|
||||
className="w-32"
|
||||
options={[
|
||||
{ value: 'both', label: 'Both' },
|
||||
{ value: 'long', label: 'Long' },
|
||||
{ value: 'short', label: 'Short' },
|
||||
]}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Min Confidence" htmlFor="min-confidence">
|
||||
<Input
|
||||
@@ -201,18 +204,20 @@ export function SetupsPanel() {
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Recommended Action" htmlFor="action">
|
||||
<Select
|
||||
<Dropdown
|
||||
id="action"
|
||||
value={actionFilter}
|
||||
onChange={(e) => setActionFilter(e.target.value as ActionFilter)}
|
||||
>
|
||||
<option value="all">All</option>
|
||||
<option value="LONG_HIGH">{RECOMMENDATION_ACTION_LABELS.LONG_HIGH}</option>
|
||||
<option value="LONG_MODERATE">{RECOMMENDATION_ACTION_LABELS.LONG_MODERATE}</option>
|
||||
<option value="SHORT_HIGH">{RECOMMENDATION_ACTION_LABELS.SHORT_HIGH}</option>
|
||||
<option value="SHORT_MODERATE">{RECOMMENDATION_ACTION_LABELS.SHORT_MODERATE}</option>
|
||||
<option value="NEUTRAL">{RECOMMENDATION_ACTION_LABELS.NEUTRAL}</option>
|
||||
</Select>
|
||||
onChange={(v) => setActionFilter(v as ActionFilter)}
|
||||
className="w-56"
|
||||
options={[
|
||||
{ value: 'all', label: 'All' },
|
||||
{ value: 'LONG_HIGH', label: RECOMMENDATION_ACTION_LABELS.LONG_HIGH },
|
||||
{ value: 'LONG_MODERATE', label: RECOMMENDATION_ACTION_LABELS.LONG_MODERATE },
|
||||
{ value: 'SHORT_HIGH', label: RECOMMENDATION_ACTION_LABELS.SHORT_HIGH },
|
||||
{ value: 'SHORT_MODERATE', label: RECOMMENDATION_ACTION_LABELS.SHORT_MODERATE },
|
||||
{ value: 'NEUTRAL', label: RECOMMENDATION_ACTION_LABELS.NEUTRAL },
|
||||
]}
|
||||
/>
|
||||
</Field>
|
||||
<div className="ml-auto">
|
||||
<Button onClick={() => scanMutation.mutate()} loading={scanMutation.isPending}>
|
||||
|
||||
Reference in New Issue
Block a user