mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-11-05 17:07:13 +02:00
fix(sentiment): send Jev requests to the endpoint TYPESAFE_BASE_URL names (#1416)
- post screening reads TypeSafe's own TYPESAFE_BASE_URL and appends /v1/systemone, as the TypeSafe SDKs do, so Jev can be reached through OpenRouter; the key still turns screening on
This commit is contained in:
@@ -25,6 +25,7 @@ NVIDIA_API_KEY=
|
||||
# TypeSafe Jev: screens StockTwits and Reddit posts for the Sentiment Analyst. Optional.
|
||||
#TYPESAFE_API_KEY=
|
||||
#TYPESAFE_DEFAULT_MODEL=jev-latest
|
||||
#TYPESAFE_BASE_URL=https://openrouter.ai/api # Jev through OpenRouter, with an OpenRouter key above
|
||||
|
||||
# Custom OpenAI-compatible endpoint (vLLM, LM Studio, llama.cpp). Local servers need no key.
|
||||
#OPENAI_COMPATIBLE_API_KEY=
|
||||
|
||||
@@ -188,7 +188,7 @@ For local models, configure Ollama with `llm_provider: "ollama"`. The default en
|
||||
|
||||
For any other OpenAI-compatible server (vLLM, LM Studio, llama.cpp, or a custom relay), use `llm_provider: "openai_compatible"` and set the endpoint via `backend_url` (or `TRADINGAGENTS_LLM_BACKEND_URL`), e.g. `http://localhost:8000/v1` for vLLM or `http://localhost:1234/v1` for LM Studio. The model is whatever your server serves. No key is needed for local servers; set `OPENAI_COMPATIBLE_API_KEY` when the endpoint requires one.
|
||||
|
||||
With `TYPESAFE_API_KEY` set, the Sentiment Analyst screens StockTwits and Reddit posts with TypeSafe's Jev before reading them. Posts that are not about the company are dropped, and each source opens with a count of the remaining posts by stance: bullish, bearish, neutral, or unclear. Without the key, posts pass through unscreened. `jev-latest` moves with new releases; set `TYPESAFE_DEFAULT_MODEL` to a versioned ID such as `jev-1.13.0` to hold it fixed across runs.
|
||||
With `TYPESAFE_API_KEY` set, the Sentiment Analyst screens StockTwits and Reddit posts with TypeSafe's Jev before reading them. Posts that are not about the company are dropped, and each source opens with a count of the remaining posts by stance: bullish, bearish, neutral, or unclear. Without the key, posts pass through unscreened. `jev-latest` moves with new releases; set `TYPESAFE_DEFAULT_MODEL` to a versioned ID such as `jev-1.13.0` to hold it fixed across runs. To reach Jev through OpenRouter, put an OpenRouter key in `TYPESAFE_API_KEY` and set `TYPESAFE_BASE_URL=https://openrouter.ai/api`.
|
||||
|
||||
Alternatively, copy `.env.example` to `.env` and fill in your keys:
|
||||
```bash
|
||||
|
||||
@@ -45,6 +45,7 @@ def post(monkeypatch):
|
||||
monkeypatch.setattr(typesafe.time, "sleep", calls.sleeps.append)
|
||||
monkeypatch.setenv("TYPESAFE_API_KEY", "ts-test")
|
||||
monkeypatch.delenv("TYPESAFE_DEFAULT_MODEL", raising=False)
|
||||
monkeypatch.delenv("TYPESAFE_BASE_URL", raising=False)
|
||||
return calls
|
||||
|
||||
|
||||
@@ -76,6 +77,26 @@ def test_the_model_follows_the_sdk_environment(post, monkeypatch):
|
||||
assert post[0][1]["json"]["model"] == "jev-1.13.0"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.parametrize("base", ["https://openrouter.ai/api", "https://openrouter.ai/api/"])
|
||||
def test_the_endpoint_follows_the_sdk_environment(post, monkeypatch, base):
|
||||
"""TypeSafe's SDKs read TYPESAFE_BASE_URL and append /v1/systemone; OpenRouter
|
||||
serves Jev at https://openrouter.ai/api with an OpenRouter key (#1416)."""
|
||||
monkeypatch.setenv("TYPESAFE_BASE_URL", base)
|
||||
post.queue.append(_ok())
|
||||
|
||||
typesafe.system_one("s", QUESTIONS)
|
||||
|
||||
assert post[0][0] == "https://openrouter.ai/api/v1/systemone"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_a_base_url_without_a_key_does_not_turn_screening_on(monkeypatch):
|
||||
monkeypatch.delenv("TYPESAFE_API_KEY", raising=False)
|
||||
monkeypatch.setenv("TYPESAFE_BASE_URL", "https://openrouter.ai/api")
|
||||
assert typesafe.jev_screen("AAPL") is None
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.parametrize("transient", [
|
||||
_Response(429), _Response(529), requests.ConnectionError(), requests.Timeout(),
|
||||
|
||||
@@ -6,8 +6,9 @@ lean on the instrument's stock. Code turns the answers into what the Sentiment
|
||||
Analyst reads: posts that are clearly about something else are dropped, and a
|
||||
stance count over the rest heads the source's block.
|
||||
|
||||
Configured by TypeSafe's own SDK variables, ``TYPESAFE_API_KEY`` and
|
||||
``TYPESAFE_DEFAULT_MODEL``. Without a key nothing here runs; if any request fails, the source's posts are kept unscreened and the
|
||||
Configured by TypeSafe's own SDK variables, ``TYPESAFE_API_KEY``,
|
||||
``TYPESAFE_DEFAULT_MODEL`` and ``TYPESAFE_BASE_URL`` (OpenRouter serves Jev at
|
||||
``https://openrouter.ai/api``). Without a key nothing here runs; if any request fails, the source's posts are kept unscreened and the
|
||||
block says screening was unavailable.
|
||||
"""
|
||||
|
||||
@@ -23,7 +24,7 @@ from tradingagents.agents.context import resolve_instrument_identity
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_URL = "https://api.typesafe.ai/v1/systemone"
|
||||
_DEFAULT_BASE_URL = "https://api.typesafe.ai"
|
||||
_DEFAULT_MODEL = "jev-latest"
|
||||
_RETRY_STATUSES = (429, 529) # rate limited, overloaded: back off and retry
|
||||
_TRANSIENT = (requests.ConnectionError, requests.Timeout, requests.exceptions.ChunkedEncodingError)
|
||||
@@ -83,7 +84,7 @@ def system_one(state, questions: dict) -> dict[str, dict]:
|
||||
time.sleep(retry_after if retry_after is not None else backoff * random.uniform(0.8, 1.2))
|
||||
backoff *= 2
|
||||
try:
|
||||
response = requests.post(_URL, json=body, headers=headers, timeout=_TIMEOUT)
|
||||
response = requests.post(_url(), json=body, headers=headers, timeout=_TIMEOUT)
|
||||
except requests.RequestException as exc:
|
||||
failure, retry_after = type(exc).__name__, None
|
||||
if isinstance(exc, _TRANSIENT):
|
||||
@@ -123,6 +124,12 @@ def _stance(answer: dict) -> str:
|
||||
return choice
|
||||
|
||||
|
||||
def _url() -> str:
|
||||
"""System One's endpoint: the base TypeSafe's SDKs read, plus /v1/systemone."""
|
||||
base = os.environ.get("TYPESAFE_BASE_URL") or _DEFAULT_BASE_URL
|
||||
return base.rstrip("/") + "/v1/systemone"
|
||||
|
||||
|
||||
def jev_screen(ticker: str):
|
||||
"""A post screen for the social fetchers, or None without a TypeSafe key.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user