Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions fintick/aggregate.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
LLM_API_KEY = os.environ.get("FINTICK_LLM_API_KEY", "ollama")
LLM_MODEL = os.environ.get("FINTICK_LLM_MODEL", "qwen3.8:27b")
DEFAULT_MODEL = LLM_MODEL # backward-compat alias
# Reasoning models (GPT-5.x) spend this budget on hidden reasoning BEFORE emitting output, so it
# must leave room for both. 4096 gets fully consumed by reasoning on hard batches -> empty content;
# 16384 leaves ample headroom (observed ~5-7k total). Tunable per-model via the env var.
LLM_MAX_TOKENS = int(os.environ.get("FINTICK_LLM_MAX_TOKENS", "16384"))
WINDOW = timedelta(hours=6)
MAX_POSTS = 200
DEFAULT_BATCH = 50
Expand Down Expand Up @@ -416,8 +420,8 @@ def call_inference(
"model": model,
"stream": False,
# GPT-5.x requires max_completion_tokens (not max_tokens) and rejects any non-default
# temperature; response_format json_object constrains output regardless.
"max_completion_tokens": 4096,
# temperature; it also spends this budget on hidden reasoning first, so keep headroom.
"max_completion_tokens": LLM_MAX_TOKENS,
"response_format": {"type": "json_object"},
"messages": [
{"role": "system", "content": SYSTEM_PROMPT},
Expand Down
2 changes: 1 addition & 1 deletion tests/test_aggregate.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def test_posts_openai_compatible_forced_json_request(self, urlopen: mock.Mock) -
self.assertEqual(payload["model"], "m-1")
self.assertIs(payload["stream"], False)
self.assertEqual(payload["response_format"], {"type": "json_object"})
self.assertLessEqual(payload["max_completion_tokens"], 4096)
self.assertEqual(payload["max_completion_tokens"], 16384)

@mock.patch("fintick.aggregate.urllib.request.urlopen")
def test_empty_content_raises(self, urlopen: mock.Mock) -> None:
Expand Down