Fix xAI sentiment: use Agent Tools web_search (Live Search deprecated)
xAI returned 410 — search_parameters/Live Search is retired. Route xAI through the Responses API web_search tool instead (same path as OpenAI): - OpenAISentimentProvider parametrized with base_url / tool_type / source - xAI builds it against https://api.x.ai/v1 with the web_search tool - Drop the dead Live Search code from the generic compatible provider - Frontend label: "xAI Grok — web search" Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -39,21 +39,6 @@ Rules:
|
||||
- reasoning should be a brief one-sentence explanation
|
||||
"""
|
||||
|
||||
_SENTIMENT_PROMPT_SEARCH = """\
|
||||
Search the web and X for the LATEST news, analyst opinions, and market developments \
|
||||
about the stock ticker {ticker} from the past 24-48 hours.
|
||||
|
||||
Based on your search findings, analyze the CURRENT market sentiment.
|
||||
|
||||
Respond ONLY with a JSON object in this exact format (no markdown, no extra text):
|
||||
{{"classification": "<bullish|bearish|neutral>", "confidence": <0-100>, "reasoning": "<brief explanation citing recent news>"}}
|
||||
|
||||
Rules:
|
||||
- classification must be exactly one of: bullish, bearish, neutral
|
||||
- confidence must be an integer from 0 to 100
|
||||
- reasoning should cite specific recent news or events you found
|
||||
"""
|
||||
|
||||
VALID_CLASSIFICATIONS = {"bullish", "bearish", "neutral"}
|
||||
|
||||
|
||||
@@ -75,8 +60,6 @@ class OpenAICompatibleSentimentProvider:
|
||||
model: str,
|
||||
base_url: str,
|
||||
source: str = "openai_compatible",
|
||||
live_search: bool = False,
|
||||
extra_body: dict | None = None,
|
||||
) -> None:
|
||||
if not api_key:
|
||||
raise ProviderError("API key is required")
|
||||
@@ -92,29 +75,8 @@ class OpenAICompatibleSentimentProvider:
|
||||
self._client = AsyncOpenAI(api_key=api_key, base_url=base_url, http_client=http_client)
|
||||
self._model = model
|
||||
self._source = source
|
||||
self._live_search = live_search
|
||||
self._extra_body = extra_body
|
||||
|
||||
@staticmethod
|
||||
def _extract_citations(response: object) -> list[dict[str, str]]:
|
||||
"""Best-effort extraction of xAI Live Search citations (list of URLs)."""
|
||||
raw = getattr(response, "citations", None)
|
||||
if not raw:
|
||||
extra = getattr(response, "model_extra", None) or {}
|
||||
raw = extra.get("citations") if isinstance(extra, dict) else None
|
||||
citations: list[dict[str, str]] = []
|
||||
for item in raw or []:
|
||||
if isinstance(item, str):
|
||||
citations.append({"url": item, "title": ""})
|
||||
elif isinstance(item, dict) and item.get("url"):
|
||||
citations.append({"url": str(item["url"]), "title": str(item.get("title", ""))})
|
||||
return citations
|
||||
|
||||
async def fetch_sentiment(self, ticker: str) -> SentimentData:
|
||||
prompt = _SENTIMENT_PROMPT_SEARCH if self._live_search else _SENTIMENT_PROMPT
|
||||
kwargs: dict = {}
|
||||
if self._extra_body:
|
||||
kwargs["extra_body"] = self._extra_body
|
||||
try:
|
||||
response = await self._client.chat.completions.create(
|
||||
model=self._model,
|
||||
@@ -123,10 +85,9 @@ class OpenAICompatibleSentimentProvider:
|
||||
"role": "system",
|
||||
"content": "You are a financial sentiment analyst. Always respond with valid JSON only, no markdown fences.",
|
||||
},
|
||||
{"role": "user", "content": prompt.format(ticker=ticker)},
|
||||
{"role": "user", "content": _SENTIMENT_PROMPT.format(ticker=ticker)},
|
||||
],
|
||||
temperature=0.3,
|
||||
**kwargs,
|
||||
)
|
||||
raw_text = (response.choices[0].message.content or "").strip()
|
||||
if not raw_text:
|
||||
@@ -155,7 +116,6 @@ class OpenAICompatibleSentimentProvider:
|
||||
source=self._source,
|
||||
timestamp=datetime.now(timezone.utc),
|
||||
reasoning=reasoning,
|
||||
citations=self._extract_citations(response) if self._live_search else [],
|
||||
)
|
||||
|
||||
except json.JSONDecodeError as exc:
|
||||
|
||||
Reference in New Issue
Block a user