synth-ai 0.2.12__py3-none-any.whl → 0.2.13.dev2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of synth-ai might be problematic. Click here for more details.
- examples/multi_step/configs/crafter_rl_outcome.toml +74 -0
- examples/multi_step/configs/crafter_rl_stepwise_hosted_judge.toml +186 -0
- examples/multi_step/configs/crafter_rl_stepwise_shaped.toml +83 -0
- examples/multi_step/configs/crafter_rl_stepwise_simple.toml +78 -0
- examples/multi_step/crafter_rl_lora.md +51 -10
- examples/multi_step/sse_metrics_streaming_notes.md +357 -0
- examples/multi_step/task_app_config_notes.md +7 -1
- examples/swe/task_app/grpo_swe_mini.py +55 -26
- examples/swe/task_app/hosted/rollout.py +40 -0
- examples/swe/task_app/hosted/test_service.py +5 -6
- examples/task_apps/TESTING.md +275 -0
- examples/task_apps/__init__.py +0 -0
- examples/task_apps/crafter/__init__.py +0 -0
- examples/task_apps/crafter/task_app/__init__.py +2 -0
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/grpo_crafter.py +21 -46
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/grpo_crafter_task_app.py +1 -1
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/policy.py +60 -4
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/inference/openai_client.py +109 -45
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/policy_routes.py +67 -49
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/rollout.py +242 -193
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/test_service.py +5 -6
- examples/task_apps/dev/pokemon_emerald/__init__.py +2 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/README.md +811 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/__init__.py +120 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/action.py +160 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/memory.py +155 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/perception.py +69 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/planning.py +96 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/simple.py +1502 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/system_prompt.py +4 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/grab_map.py +68 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/manual.py +216 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/__init__.py +35 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/emerald_utils.py +631 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/emulator.py +1544 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/enums.py +1428 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/memory_reader.py +4848 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/types.py +41 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/utils.py +298 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pyproject.toml +95 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/run.py +204 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/__init__.py +0 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/app.py +2152 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/client.py +429 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/frame_server.py +155 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/README.md +78 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/__init__.py +0 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/run_tests.py +122 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_agent_direct.py +76 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_agent_prompts.py +413 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_battle_state_formatting.py +204 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_dialogue_detection.py +133 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_dialogue_detection_comprehensive.py +229 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_direct_agent_emulator.py +300 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_fps_adjustment_pytest.py +205 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_house_to_outside_direct.py +200 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_house_to_outside_transition.py +284 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_map_ground_truth_comparison.py +468 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_memory_map.py +575 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_server_map_validation.py +311 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_torchic_state.py +259 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/__init__.py +0 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/anticheat.py +372 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/checkpoint.py +296 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/error_handler.py +275 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/get_local_ip.py +22 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/helpers.py +44 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/llm_logger.py +514 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_formatter.py +415 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_stitcher.py +1763 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_stitcher_singleton.py +33 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_trimmer.py +106 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_visualizer.py +334 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/ocr_dialogue.py +1020 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/recording.py +188 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/state_formatter.py +1481 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/vlm.py +862 -0
- examples/task_apps/dev/pokemon_emerald/modal_app.py +114 -0
- examples/task_apps/dev/pokemon_emerald/task_app/README.md +81 -0
- examples/task_apps/dev/pokemon_emerald/task_app/__init__.py +6 -0
- examples/task_apps/dev/pokemon_emerald/task_app/pokemon_emerald.py +685 -0
- examples/task_apps/enron/__init__.py +1 -0
- examples/task_apps/enron/eval_groq_qwen32.toml +16 -0
- examples/task_apps/enron/task_app/README.md +14 -0
- examples/task_apps/enron/task_app/__init__.py +1 -0
- examples/task_apps/enron/task_app/grpo_enron.py +906 -0
- examples/task_apps/enron/task_app/grpo_enron_task_app.py +146 -0
- examples/task_apps/enron/tests/__init__.py +2 -0
- examples/task_apps/enron/tests/conftest.py +115 -0
- examples/task_apps/enron/tests/integration/__init__.py +2 -0
- examples/task_apps/enron/tests/integration/test_enron_eval.py +177 -0
- examples/task_apps/enron/tests/integration/test_enron_rollout.py +135 -0
- examples/task_apps/enron/tests/unit/__init__.py +2 -0
- examples/task_apps/enron/tests/unit/test_enron_environment.py +126 -0
- examples/task_apps/math/__init__.py +0 -0
- examples/{rl/task_app → task_apps/math}/math_single_step.py +19 -10
- examples/task_apps/pokemon_battle/__init__.py +2 -0
- examples/task_apps/pokemon_battle/modal_app.py +104 -0
- examples/task_apps/pokemon_battle/task_app/README.md +68 -0
- examples/task_apps/pokemon_battle/task_app/__init__.py +6 -0
- examples/task_apps/pokemon_battle/task_app/pokemon_showdown.py +932 -0
- examples/task_apps/pokemon_red/README.md +357 -0
- examples/task_apps/pokemon_red/__init__.py +3 -0
- examples/task_apps/pokemon_red/eval_pokemon_red_policy.py +225 -0
- examples/task_apps/pokemon_red/pallet_town_rl_config.toml +73 -0
- examples/task_apps/pokemon_red/task_app.py +606 -0
- examples/task_apps/pokemon_red/test_pallet_town_rewards.py +191 -0
- examples/task_apps/sokoban/README.md +307 -0
- examples/task_apps/sokoban/__init__.py +3 -0
- examples/task_apps/sokoban/eval_groq_qwen32.toml +16 -0
- examples/task_apps/sokoban/eval_openai_gpt5.toml +16 -0
- examples/task_apps/sokoban/task_app.py +1058 -0
- examples/task_apps/sokoban/tests/__init__.py +2 -0
- examples/task_apps/sokoban/tests/conftest.py +113 -0
- examples/task_apps/sokoban/tests/integration/__init__.py +2 -0
- examples/task_apps/sokoban/tests/integration/test_sokoban_eval.py +57 -0
- examples/task_apps/sokoban/tests/integration/test_sokoban_rollout.py +198 -0
- examples/task_apps/sokoban/tests/unit/__init__.py +2 -0
- examples/task_apps/sokoban/tests/unit/test_sokoban_environment.py +114 -0
- examples/task_apps/verilog/__init__.py +1 -0
- examples/task_apps/verilog/eval_groq_qwen32b.toml +20 -0
- examples/task_apps/verilog/task_app/README.md +12 -0
- examples/task_apps/verilog/task_app/__init__.py +1 -0
- examples/task_apps/verilog/task_app/grpo_verilog.py +931 -0
- examples/task_apps/verilog/task_app/grpo_verilog_task_app.py +145 -0
- examples/task_apps/verilog/tests/__init__.py +2 -0
- examples/task_apps/verilog/tests/conftest.py +115 -0
- examples/task_apps/verilog/tests/integration/__init__.py +2 -0
- examples/task_apps/verilog/tests/integration/test_verilog_eval.py +179 -0
- examples/task_apps/verilog/tests/integration/test_verilog_rollout.py +55 -0
- examples/task_apps/verilog/tests/unit/__init__.py +2 -0
- examples/task_apps/verilog/tests/unit/test_verilog_scoring.py +118 -0
- examples/vlm/crafter_openai_vlm_agent.py +4 -4
- examples/vlm/run_crafter_vlm_benchmark.py +4 -4
- examples/warming_up_to_rl/configs/eval_stepwise_complex.toml +4 -2
- examples/warming_up_to_rl/configs/eval_stepwise_simple.toml +4 -2
- examples/warming_up_to_rl/run_eval.py +127 -18
- examples/workflows/__init__.py +0 -0
- examples/workflows/math_rl/__init__.py +0 -0
- examples/workflows/math_rl/download_dataset.py +80 -0
- synth_ai/__init__.py +41 -1
- synth_ai/api/train/builders.py +73 -29
- synth_ai/api/train/cli.py +12 -6
- synth_ai/api/train/configs/__init__.py +44 -0
- synth_ai/api/train/configs/rl.py +134 -0
- synth_ai/api/train/configs/sft.py +95 -0
- synth_ai/api/train/configs/shared.py +24 -0
- synth_ai/api/train/env_resolver.py +5 -2
- synth_ai/api/train/supported_algos.py +10 -5
- synth_ai/api/train/utils.py +7 -4
- synth_ai/cli/__init__.py +7 -51
- synth_ai/cli/_storage.py +4 -3
- synth_ai/cli/_validate_task_app.py +11 -0
- synth_ai/cli/balance.py +4 -3
- synth_ai/cli/calc.py +2 -2
- synth_ai/cli/demo.py +49 -43
- synth_ai/cli/legacy_root_backup.py +1 -1
- synth_ai/cli/rl_demo.py +86 -106
- synth_ai/cli/root.py +0 -97
- synth_ai/cli/task_apps.py +1710 -186
- synth_ai/demos/core/cli.py +121 -159
- synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +28 -16
- synth_ai/environments/examples/crafter_classic/environment.py +16 -0
- synth_ai/environments/examples/enron/engine.py +7 -2
- synth_ai/environments/examples/enron/environment.py +68 -0
- synth_ai/environments/examples/red/engine.py +27 -0
- synth_ai/environments/examples/red/engine_helpers/memory_map.py +7 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_progression.py +477 -0
- synth_ai/environments/examples/red/engine_helpers/state_extraction.py +32 -0
- synth_ai/environments/examples/red/environment.py +60 -0
- synth_ai/environments/examples/sokoban/taskset.py +116 -0
- synth_ai/environments/examples/verilog/engine.py +30 -4
- synth_ai/evals/__init__.py +15 -0
- synth_ai/evals/client.py +82 -0
- synth_ai/evals/types.py +42 -0
- synth_ai/jobs/client.py +16 -4
- synth_ai/judge_schemas.py +127 -0
- synth_ai/py.typed +0 -0
- synth_ai/task/__init__.py +14 -5
- synth_ai/task/contracts.py +124 -38
- synth_ai/task/proxy.py +48 -56
- synth_ai/task/rubrics/__init__.py +53 -0
- synth_ai/task/rubrics/loaders.py +133 -0
- synth_ai/task/rubrics/models.py +57 -0
- synth_ai/task/rubrics/scoring.py +113 -0
- synth_ai/task/rubrics/strict.py +149 -0
- synth_ai/task/server.py +8 -7
- synth_ai/task/validators.py +269 -6
- synth_ai/tracing_v3/decorators.py +7 -3
- synth_ai/tracing_v3/replica_sync.py +4 -4
- synth_ai/tracing_v3/serialization.py +130 -0
- synth_ai/tracing_v3/trace_utils.py +317 -0
- synth_ai/tracing_v3/turso/native_manager.py +3 -3
- {synth_ai-0.2.12.dist-info → synth_ai-0.2.13.dev2.dist-info}/METADATA +4 -1
- {synth_ai-0.2.12.dist-info → synth_ai-0.2.13.dev2.dist-info}/RECORD +228 -89
- {synth_ai-0.2.12.dist-info → synth_ai-0.2.13.dev2.dist-info}/entry_points.txt +0 -1
- synth_ai/task/rubrics.py +0 -219
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/README.md +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/README.md +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/__init__.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/branching.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/environment_routes.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/__init__.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/__init__.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/app.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/environment.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/react_agent.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/shared.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/tools.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/hosted_app.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/inference/__init__.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/main.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/registry.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/storage/__init__.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/storage/volume.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/test_agents.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/utils.py +0 -0
- /examples/{rl/task_app → task_apps/math}/README.md +0 -0
- /examples/{rl/task_app → task_apps/math}/math_task_app.py +0 -0
- /examples/{rl → workflows/math_rl}/configs/eval_base_qwen.toml +0 -0
- /examples/{rl → workflows/math_rl}/configs/eval_rl_qwen.toml +0 -0
- /examples/{rl → workflows/math_rl}/configs/rl_from_base_qwen.toml +0 -0
- /examples/{rl → workflows/math_rl}/configs/rl_from_base_qwen17.toml +0 -0
- /examples/{rl → workflows/math_rl}/configs/rl_from_ft_qwen.toml +0 -0
- /examples/{rl → workflows/math_rl}/run_eval.py +0 -0
- /examples/{rl → workflows/math_rl}/run_rl_and_save.py +0 -0
- {synth_ai-0.2.12.dist-info → synth_ai-0.2.13.dev2.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.12.dist-info → synth_ai-0.2.13.dev2.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.2.12.dist-info → synth_ai-0.2.13.dev2.dist-info}/top_level.txt +0 -0
|
@@ -2,7 +2,10 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import contextlib
|
|
5
|
+
import json
|
|
5
6
|
import logging
|
|
7
|
+
import os
|
|
8
|
+
import time
|
|
6
9
|
from typing import Any
|
|
7
10
|
|
|
8
11
|
import httpx
|
|
@@ -23,9 +26,15 @@ class OpenAIClient:
|
|
|
23
26
|
self.api_key = api_key
|
|
24
27
|
self.timeout_s = timeout_s
|
|
25
28
|
self.headers = {}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
# If we're calling back into our own task app proxy (e.g., /proxy/groq),
|
|
30
|
+
# the FastAPI app still enforces X-API-Key. Include it when available so
|
|
31
|
+
# intra-app proxy calls authenticate correctly.
|
|
32
|
+
try:
|
|
33
|
+
env_key = os.getenv("ENVIRONMENT_API_KEY")
|
|
34
|
+
if env_key and isinstance(env_key, str):
|
|
35
|
+
self.headers.setdefault("X-API-Key", env_key)
|
|
36
|
+
except Exception:
|
|
37
|
+
pass
|
|
29
38
|
|
|
30
39
|
def _fix_model_parameters(
|
|
31
40
|
self, request: dict[str, Any], target_url: str | None = None
|
|
@@ -52,6 +61,8 @@ class OpenAIClient:
|
|
|
52
61
|
or ("azure" in low and ".openai." in low)
|
|
53
62
|
or ("groq.com" in low)
|
|
54
63
|
or ("/openai" in low)
|
|
64
|
+
or ("/proxy/groq" in low)
|
|
65
|
+
or ("/proxy/openai" in low)
|
|
55
66
|
)
|
|
56
67
|
except Exception:
|
|
57
68
|
is_openai = False
|
|
@@ -137,13 +148,53 @@ class OpenAIClient:
|
|
|
137
148
|
Returns:
|
|
138
149
|
OpenAI-compatible chat completion response
|
|
139
150
|
"""
|
|
140
|
-
|
|
151
|
+
base = (base_url or self.base_url).rstrip("/")
|
|
152
|
+
url = base + "/v1/chat/completions"
|
|
141
153
|
timeout = timeout_s or self.timeout_s
|
|
142
154
|
|
|
143
155
|
# Merge headers
|
|
144
156
|
headers = self.headers.copy()
|
|
145
157
|
if extra_headers:
|
|
146
158
|
headers.update(extra_headers)
|
|
159
|
+
# Always include X-API-Key for intra-app requests
|
|
160
|
+
try:
|
|
161
|
+
envk = os.getenv("ENVIRONMENT_API_KEY")
|
|
162
|
+
if envk and isinstance(envk, str):
|
|
163
|
+
headers["X-API-Key"] = envk
|
|
164
|
+
except Exception:
|
|
165
|
+
pass
|
|
166
|
+
|
|
167
|
+
# If target is our in-app Groq proxy, force Authorization to use GROQ_API_KEY
|
|
168
|
+
try:
|
|
169
|
+
low_url = (url or "").lower()
|
|
170
|
+
if "/proxy/groq" in low_url or "groq" in low_url:
|
|
171
|
+
gk = os.getenv("GROQ_API_KEY")
|
|
172
|
+
if gk and isinstance(gk, str):
|
|
173
|
+
headers["Authorization"] = f"Bearer {gk}"
|
|
174
|
+
except Exception:
|
|
175
|
+
pass
|
|
176
|
+
|
|
177
|
+
# In-process proxy path: avoid HTTP round-trip and auth dependency
|
|
178
|
+
try:
|
|
179
|
+
if base.endswith("/proxy/groq") or base.endswith("/proxy/groq/"):
|
|
180
|
+
from synth_ai.task.server import prepare_for_groq, inject_system_hint
|
|
181
|
+
# Prepare payload similar to server-side proxy
|
|
182
|
+
model = request.get("model") if isinstance(request.get("model"), str) else None
|
|
183
|
+
payload = prepare_for_groq(model, request)
|
|
184
|
+
payload = inject_system_hint(payload, "")
|
|
185
|
+
# Call vendor directly
|
|
186
|
+
gk = os.getenv("GROQ_API_KEY") or ""
|
|
187
|
+
async with httpx.AsyncClient(timeout=timeout) as client:
|
|
188
|
+
resp = await client.post(
|
|
189
|
+
"https://api.groq.com/openai/v1/chat/completions",
|
|
190
|
+
json=payload,
|
|
191
|
+
headers={"Authorization": f"Bearer {gk}"},
|
|
192
|
+
)
|
|
193
|
+
resp.raise_for_status()
|
|
194
|
+
return resp.json()
|
|
195
|
+
except Exception as _local_proxy_err:
|
|
196
|
+
# Do NOT fall back silently; surface the error so callers fail fast
|
|
197
|
+
raise
|
|
147
198
|
|
|
148
199
|
# Fix parameter compatibility for newer models
|
|
149
200
|
processed_request = self._fix_model_parameters(request, target_url=url)
|
|
@@ -227,11 +278,7 @@ class OpenAIClient:
|
|
|
227
278
|
logger.info(
|
|
228
279
|
f"Inference response status=200, content-type={content_type}, bytes={len(body_text)}"
|
|
229
280
|
)
|
|
230
|
-
|
|
231
|
-
preview_len = min(800, len(body_text))
|
|
232
|
-
logger.info(
|
|
233
|
-
f"Inference response preview ({preview_len} bytes): {body_text[:preview_len]}"
|
|
234
|
-
)
|
|
281
|
+
# Do not log prompt or full response body
|
|
235
282
|
|
|
236
283
|
result = response.json()
|
|
237
284
|
logger.info(f"Inference response parsed_type={type(result).__name__}")
|
|
@@ -243,34 +290,10 @@ class OpenAIClient:
|
|
|
243
290
|
except httpx.HTTPStatusError as e:
|
|
244
291
|
status = e.response.status_code if e.response is not None else None
|
|
245
292
|
text = e.response.text if e.response is not None else str(e)
|
|
246
|
-
# Log
|
|
247
|
-
|
|
248
|
-
logger.error(
|
|
249
|
-
{
|
|
250
|
-
"openai_http_error": True,
|
|
251
|
-
"status": status,
|
|
252
|
-
"url": url,
|
|
253
|
-
"body": text,
|
|
254
|
-
}
|
|
255
|
-
)
|
|
256
|
-
except Exception:
|
|
257
|
-
logger.error(f"HTTP error from {url}: {status} - {text}")
|
|
293
|
+
# Log minimal error info only
|
|
294
|
+
logger.error({"openai_http_error": True, "status": status})
|
|
258
295
|
# For 4xx/5xx, print full sanitized request to aid debugging (especially Groq 400s)
|
|
259
|
-
|
|
260
|
-
redacted_headers = dict(headers)
|
|
261
|
-
if "Authorization" in redacted_headers:
|
|
262
|
-
redacted_headers["Authorization"] = "***REDACTED***"
|
|
263
|
-
logger.error(
|
|
264
|
-
{
|
|
265
|
-
"request_debug": True,
|
|
266
|
-
"status": status,
|
|
267
|
-
"target": url,
|
|
268
|
-
"headers": redacted_headers,
|
|
269
|
-
"payload": processed_request,
|
|
270
|
-
}
|
|
271
|
-
)
|
|
272
|
-
except Exception:
|
|
273
|
-
pass
|
|
296
|
+
# Suppress prompt/payload logging entirely
|
|
274
297
|
# Special case: token budget exceeded (OpenAI-compatible error schema)
|
|
275
298
|
try:
|
|
276
299
|
if status == 400 and e.response is not None:
|
|
@@ -324,8 +347,6 @@ class OpenAIClient:
|
|
|
324
347
|
logger.warning(
|
|
325
348
|
{
|
|
326
349
|
"token_budget_recovery": True,
|
|
327
|
-
"messages_tokens": messages_tokens,
|
|
328
|
-
"model_limit": model_limit,
|
|
329
350
|
"retry_max_tokens": new_max,
|
|
330
351
|
}
|
|
331
352
|
)
|
|
@@ -348,13 +369,8 @@ class OpenAIClient:
|
|
|
348
369
|
try:
|
|
349
370
|
err = e.response.json()
|
|
350
371
|
except Exception:
|
|
351
|
-
err = {"error": "unprocessable"
|
|
352
|
-
logger.warning(
|
|
353
|
-
{
|
|
354
|
-
"inference_422_recovered": True,
|
|
355
|
-
"detail": err,
|
|
356
|
-
}
|
|
357
|
-
)
|
|
372
|
+
err = {"error": "unprocessable"}
|
|
373
|
+
logger.warning({"inference_422_recovered": True})
|
|
358
374
|
except Exception:
|
|
359
375
|
pass
|
|
360
376
|
# Return a minimal OpenAI-compatible response with no tool_calls/content
|
|
@@ -471,6 +487,54 @@ class OpenAIClient:
|
|
|
471
487
|
f"Inference service overloaded (400). {response_data} Retrying after {wait_time}s..."
|
|
472
488
|
)
|
|
473
489
|
else:
|
|
490
|
+
error_block = response_data.get("error")
|
|
491
|
+
error_code = ""
|
|
492
|
+
if isinstance(error_block, dict):
|
|
493
|
+
error_code = str(
|
|
494
|
+
error_block.get("code") or error_block.get("type") or ""
|
|
495
|
+
).lower()
|
|
496
|
+
if error_code in {"tool_use_failed", "tool_call_failed"}:
|
|
497
|
+
logger.warning(
|
|
498
|
+
{
|
|
499
|
+
"tool_use_failed": True,
|
|
500
|
+
"target": (base_url or self.base_url),
|
|
501
|
+
"message": error_block.get("message") if isinstance(error_block, dict) else None,
|
|
502
|
+
}
|
|
503
|
+
)
|
|
504
|
+
fallback_actions = ["move_right", "move_up", "do"]
|
|
505
|
+
fallback_response = {
|
|
506
|
+
"id": f"fallback-{int(time.time() * 1000)}",
|
|
507
|
+
"object": "chat.completion",
|
|
508
|
+
"created": int(time.time()),
|
|
509
|
+
"model": processed_request.get("model"),
|
|
510
|
+
"choices": [
|
|
511
|
+
{
|
|
512
|
+
"index": 0,
|
|
513
|
+
"message": {
|
|
514
|
+
"role": "assistant",
|
|
515
|
+
"content": "",
|
|
516
|
+
"tool_calls": [
|
|
517
|
+
{
|
|
518
|
+
"id": f"call_fallback_{int(time.time() * 1000)}",
|
|
519
|
+
"type": "function",
|
|
520
|
+
"function": {
|
|
521
|
+
"name": "interact_many",
|
|
522
|
+
"arguments": json.dumps(
|
|
523
|
+
{"actions": fallback_actions}
|
|
524
|
+
),
|
|
525
|
+
},
|
|
526
|
+
}
|
|
527
|
+
],
|
|
528
|
+
},
|
|
529
|
+
"finish_reason": "tool_calls",
|
|
530
|
+
}
|
|
531
|
+
],
|
|
532
|
+
}
|
|
533
|
+
if isinstance(response_data.get("usage"), dict):
|
|
534
|
+
fallback_response["usage"] = response_data["usage"]
|
|
535
|
+
if isinstance(error_block, dict):
|
|
536
|
+
fallback_response["error"] = error_block
|
|
537
|
+
return fallback_response
|
|
474
538
|
# This is a different type of 400 error, don't retry
|
|
475
539
|
try:
|
|
476
540
|
redacted_headers = {}
|
|
@@ -9,6 +9,8 @@ from typing import Any
|
|
|
9
9
|
from fastapi import APIRouter, HTTPException, Request
|
|
10
10
|
from pydantic import BaseModel
|
|
11
11
|
|
|
12
|
+
from synth_ai.task.auth import allowed_environment_api_keys, normalize_environment_api_key
|
|
13
|
+
|
|
12
14
|
from .envs.crafter.policy import CrafterPolicy
|
|
13
15
|
from .inference.openai_client import create_inference_client
|
|
14
16
|
from .registry import registry
|
|
@@ -95,10 +97,32 @@ async def create_policy(
|
|
|
95
97
|
|
|
96
98
|
# Set defaults from TaskApp / environment if not provided
|
|
97
99
|
config = dict(request.config or {})
|
|
100
|
+
provider_raw = config.get("provider") or config.get("vendor")
|
|
101
|
+
provider = str(provider_raw).strip().lower() if provider_raw else None
|
|
102
|
+
|
|
103
|
+
# Resolve base URL for proxy endpoints (strip trailing slash)
|
|
104
|
+
base_url = str(req.base_url).rstrip("/")
|
|
105
|
+
|
|
106
|
+
if provider == "groq":
|
|
107
|
+
# Route through in-app Groq proxy by default
|
|
108
|
+
config.setdefault("inference_url", f"{base_url}/proxy/groq")
|
|
109
|
+
# Default to a recent Groq-hosted Qwen unless caller overrides
|
|
110
|
+
preferred_model = "qwen/qwen3-32b"
|
|
111
|
+
config.setdefault("model", preferred_model)
|
|
112
|
+
# Groq Qwen defaults tuned for deterministic tool use
|
|
113
|
+
config.setdefault("temperature", 0.0)
|
|
114
|
+
config.setdefault("top_p", 0.95)
|
|
115
|
+
config.setdefault("max_tokens", 256)
|
|
116
|
+
# Avoid leaking provider in downstream policy if unset
|
|
117
|
+
config["provider"] = "groq"
|
|
118
|
+
elif provider == "openai":
|
|
119
|
+
config.setdefault("inference_url", f"{base_url}/proxy")
|
|
120
|
+
config["provider"] = "openai"
|
|
121
|
+
|
|
98
122
|
if "inference_url" not in config and task_app is not None:
|
|
99
|
-
|
|
100
|
-
if
|
|
101
|
-
config["inference_url"] =
|
|
123
|
+
task_base_url = getattr(task_app, "vllm_base_url", None)
|
|
124
|
+
if task_base_url:
|
|
125
|
+
config["inference_url"] = task_base_url
|
|
102
126
|
if "model" not in config and task_app is not None:
|
|
103
127
|
default_model = getattr(task_app, "default_model", None)
|
|
104
128
|
if default_model:
|
|
@@ -435,34 +459,33 @@ async def step_policy(
|
|
|
435
459
|
elif role == "user":
|
|
436
460
|
user_prompt_records.append(record)
|
|
437
461
|
|
|
462
|
+
last_user_chars = (
|
|
463
|
+
len(user_prompt_records[-1].get("text", "")) if user_prompt_records else 0
|
|
464
|
+
)
|
|
438
465
|
logger.info(
|
|
439
|
-
"PROMPTS: system_msgs=%d user_msgs=%d last_user_chars=%d",
|
|
466
|
+
"PROMPTS: system_msgs=%d user_msgs=%d last_user_chars=%d (content suppressed)",
|
|
440
467
|
len(system_prompt_records),
|
|
441
468
|
len(user_prompt_records),
|
|
442
|
-
|
|
469
|
+
last_user_chars,
|
|
443
470
|
)
|
|
444
471
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
if user_prompt_records
|
|
463
|
-
else ""
|
|
464
|
-
)
|
|
465
|
-
print(f"[task:crafter] user prompt: {last_user}", flush=True)
|
|
472
|
+
log_prompt_details = (
|
|
473
|
+
os.getenv("CRAFT_LOG_PROMPTS", "").strip().lower()
|
|
474
|
+
in {"1", "true", "yes", "debug"}
|
|
475
|
+
)
|
|
476
|
+
if log_prompt_details:
|
|
477
|
+
if system_prompt_records:
|
|
478
|
+
logger.info("PROMPT_DETAILS_SYSTEM_BEGIN")
|
|
479
|
+
for idx, rec in enumerate(system_prompt_records):
|
|
480
|
+
smsg = rec.get("text", "")
|
|
481
|
+
logger.info("SYSTEM[%d]: %s", idx, smsg)
|
|
482
|
+
logger.info("PROMPT_DETAILS_SYSTEM_END")
|
|
483
|
+
if user_prompt_records:
|
|
484
|
+
logger.info("PROMPT_DETAILS_USER_BEGIN")
|
|
485
|
+
for idx, rec in enumerate(user_prompt_records):
|
|
486
|
+
umsg = rec.get("text", "")
|
|
487
|
+
logger.info("USER[%d]: %s", idx, umsg)
|
|
488
|
+
logger.info("PROMPT_DETAILS_USER_END")
|
|
466
489
|
except Exception as e:
|
|
467
490
|
logger.warning(f"PROMPT_DUMP_FAILED: {e}")
|
|
468
491
|
|
|
@@ -524,15 +547,29 @@ async def step_policy(
|
|
|
524
547
|
masked = "<masked>"
|
|
525
548
|
logger.debug(f"INFERENCE_AUTH: Using bearer key {masked}")
|
|
526
549
|
else:
|
|
527
|
-
logger.
|
|
528
|
-
"INFERENCE_AUTH: No
|
|
550
|
+
logger.debug(
|
|
551
|
+
"INFERENCE_AUTH: No bearer key resolved for inference request (expected when using in-app proxy)"
|
|
529
552
|
)
|
|
530
553
|
|
|
531
554
|
client = create_inference_client(task_app, api_key=api_key_override)
|
|
532
555
|
|
|
533
|
-
# Add policy identification header for
|
|
556
|
+
# Add policy identification header and task auth for proxy fallback
|
|
534
557
|
policy_name = getattr(policy, "name", "") or type(policy).__name__.lower()
|
|
535
558
|
extra_headers = {"X-Policy-Name": policy_name}
|
|
559
|
+
try:
|
|
560
|
+
env_key = normalize_environment_api_key()
|
|
561
|
+
if not env_key:
|
|
562
|
+
allowed_keys = allowed_environment_api_keys()
|
|
563
|
+
if allowed_keys:
|
|
564
|
+
env_key = next(iter(sorted(allowed_keys)))
|
|
565
|
+
if isinstance(env_key, str) and env_key:
|
|
566
|
+
extra_headers["X-API-Key"] = env_key
|
|
567
|
+
else:
|
|
568
|
+
logger.warning(
|
|
569
|
+
"INFERENCE_AUTH: Failed to resolve ENVIRONMENT_API_KEY for proxy request headers"
|
|
570
|
+
)
|
|
571
|
+
except Exception as exc:
|
|
572
|
+
logger.warning(f"INFERENCE_AUTH: Error resolving ENVIRONMENT_API_KEY: {exc}")
|
|
536
573
|
|
|
537
574
|
# Apply input truncation to avoid 422 from inference server
|
|
538
575
|
try:
|
|
@@ -761,26 +798,7 @@ async def step_policy(
|
|
|
761
798
|
}
|
|
762
799
|
|
|
763
800
|
# Emit the exact prompt/messages and tools before calling the LLM (bounded preview)
|
|
764
|
-
|
|
765
|
-
req_dump = meta.get("inference_request", {})
|
|
766
|
-
msgs = req_dump.get("messages")
|
|
767
|
-
tools_dump = req_dump.get("tools")
|
|
768
|
-
if isinstance(msgs, list):
|
|
769
|
-
# Print compact messages structure and tool schema with bounded length
|
|
770
|
-
import json as _json
|
|
771
|
-
|
|
772
|
-
msgs_compact = _json.dumps(msgs)[:20000]
|
|
773
|
-
tools_compact = (
|
|
774
|
-
_json.dumps(tools_dump)[:8000] if tools_dump is not None else None
|
|
775
|
-
)
|
|
776
|
-
print(
|
|
777
|
-
{
|
|
778
|
-
"llm.call": True,
|
|
779
|
-
"policy": str(policy_name),
|
|
780
|
-
"messages_preview": msgs_compact,
|
|
781
|
-
"tools_preview": tools_compact,
|
|
782
|
-
}
|
|
783
|
-
)
|
|
801
|
+
# Do not print prompts; only log response content later
|
|
784
802
|
|
|
785
803
|
# Normalize request for non-OpenAI endpoints (strict schemas)
|
|
786
804
|
with contextlib.suppress(Exception):
|