synth-ai 0.2.4.dev6__py3-none-any.whl → 0.2.4.dev8__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.
- synth_ai/__init__.py +18 -9
- synth_ai/cli/__init__.py +10 -5
- synth_ai/cli/balance.py +25 -32
- synth_ai/cli/calc.py +2 -3
- synth_ai/cli/demo.py +3 -5
- synth_ai/cli/legacy_root_backup.py +58 -32
- synth_ai/cli/man.py +22 -19
- synth_ai/cli/recent.py +9 -8
- synth_ai/cli/root.py +58 -13
- synth_ai/cli/status.py +13 -6
- synth_ai/cli/traces.py +45 -21
- synth_ai/cli/watch.py +40 -37
- synth_ai/config/base_url.py +47 -2
- synth_ai/core/experiment.py +1 -2
- synth_ai/environments/__init__.py +2 -6
- synth_ai/environments/environment/artifacts/base.py +3 -1
- synth_ai/environments/environment/db/sqlite.py +1 -1
- synth_ai/environments/environment/registry.py +19 -20
- synth_ai/environments/environment/resources/sqlite.py +2 -3
- synth_ai/environments/environment/rewards/core.py +3 -2
- synth_ai/environments/environment/tools/__init__.py +6 -4
- synth_ai/environments/examples/crafter_classic/__init__.py +1 -1
- synth_ai/environments/examples/crafter_classic/engine.py +13 -13
- synth_ai/environments/examples/crafter_classic/engine_deterministic_patch.py +1 -0
- synth_ai/environments/examples/crafter_classic/engine_helpers/action_map.py +2 -1
- synth_ai/environments/examples/crafter_classic/engine_helpers/serialization.py +2 -1
- synth_ai/environments/examples/crafter_classic/engine_serialization_patch_v3.py +3 -2
- synth_ai/environments/examples/crafter_classic/environment.py +16 -15
- synth_ai/environments/examples/crafter_classic/taskset.py +2 -2
- synth_ai/environments/examples/crafter_classic/trace_hooks_v3.py +2 -3
- synth_ai/environments/examples/crafter_classic/world_config_patch_simple.py +2 -1
- synth_ai/environments/examples/crafter_custom/crafter/__init__.py +2 -2
- synth_ai/environments/examples/crafter_custom/crafter/config.py +2 -2
- synth_ai/environments/examples/crafter_custom/crafter/env.py +1 -5
- synth_ai/environments/examples/crafter_custom/crafter/objects.py +1 -2
- synth_ai/environments/examples/crafter_custom/crafter/worldgen.py +1 -2
- synth_ai/environments/examples/crafter_custom/dataset_builder.py +5 -5
- synth_ai/environments/examples/crafter_custom/environment.py +13 -13
- synth_ai/environments/examples/crafter_custom/run_dataset.py +5 -5
- synth_ai/environments/examples/enron/art_helpers/email_search_tools.py +2 -2
- synth_ai/environments/examples/enron/art_helpers/local_email_db.py +5 -4
- synth_ai/environments/examples/enron/art_helpers/types_enron.py +2 -1
- synth_ai/environments/examples/enron/engine.py +18 -14
- synth_ai/environments/examples/enron/environment.py +12 -11
- synth_ai/environments/examples/enron/taskset.py +7 -7
- synth_ai/environments/examples/minigrid/__init__.py +6 -6
- synth_ai/environments/examples/minigrid/engine.py +6 -6
- synth_ai/environments/examples/minigrid/environment.py +6 -6
- synth_ai/environments/examples/minigrid/puzzle_loader.py +3 -2
- synth_ai/environments/examples/minigrid/taskset.py +13 -13
- synth_ai/environments/examples/nethack/achievements.py +1 -1
- synth_ai/environments/examples/nethack/engine.py +8 -7
- synth_ai/environments/examples/nethack/environment.py +10 -9
- synth_ai/environments/examples/nethack/helpers/__init__.py +8 -9
- synth_ai/environments/examples/nethack/helpers/action_mapping.py +1 -1
- synth_ai/environments/examples/nethack/helpers/nle_wrapper.py +2 -1
- synth_ai/environments/examples/nethack/helpers/observation_utils.py +1 -1
- synth_ai/environments/examples/nethack/helpers/recording_wrapper.py +3 -4
- synth_ai/environments/examples/nethack/helpers/trajectory_recorder.py +6 -5
- synth_ai/environments/examples/nethack/helpers/visualization/replay_viewer.py +5 -5
- synth_ai/environments/examples/nethack/helpers/visualization/visualizer.py +7 -6
- synth_ai/environments/examples/nethack/taskset.py +5 -5
- synth_ai/environments/examples/red/engine.py +9 -8
- synth_ai/environments/examples/red/engine_helpers/reward_components.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/__init__.py +7 -7
- synth_ai/environments/examples/red/engine_helpers/reward_library/adaptive_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/battle_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/composite_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/economy_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/efficiency_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/exploration_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/novelty_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/pokemon_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/social_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/story_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/screen_analysis.py +3 -2
- synth_ai/environments/examples/red/engine_helpers/state_extraction.py +2 -1
- synth_ai/environments/examples/red/environment.py +18 -15
- synth_ai/environments/examples/red/taskset.py +5 -3
- synth_ai/environments/examples/sokoban/engine.py +16 -13
- synth_ai/environments/examples/sokoban/engine_helpers/room_utils.py +3 -2
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/__init__.py +2 -1
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/__init__.py +1 -1
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/boxoban_env.py +7 -5
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/render_utils.py +1 -1
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/room_utils.py +2 -1
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env.py +5 -4
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_fixed_targets.py +3 -2
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_pull.py +2 -1
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_two_player.py +5 -4
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_variations.py +1 -1
- synth_ai/environments/examples/sokoban/environment.py +15 -14
- synth_ai/environments/examples/sokoban/generate_verified_puzzles.py +5 -3
- synth_ai/environments/examples/sokoban/puzzle_loader.py +3 -2
- synth_ai/environments/examples/sokoban/taskset.py +13 -10
- synth_ai/environments/examples/tictactoe/engine.py +6 -6
- synth_ai/environments/examples/tictactoe/environment.py +8 -7
- synth_ai/environments/examples/tictactoe/taskset.py +6 -5
- synth_ai/environments/examples/verilog/engine.py +4 -3
- synth_ai/environments/examples/verilog/environment.py +11 -10
- synth_ai/environments/examples/verilog/taskset.py +14 -12
- synth_ai/environments/examples/wordle/__init__.py +5 -5
- synth_ai/environments/examples/wordle/engine.py +32 -25
- synth_ai/environments/examples/wordle/environment.py +21 -16
- synth_ai/environments/examples/wordle/helpers/generate_instances_wordfreq.py +6 -6
- synth_ai/environments/examples/wordle/taskset.py +20 -12
- synth_ai/environments/reproducibility/core.py +1 -1
- synth_ai/environments/reproducibility/tree.py +21 -21
- synth_ai/environments/service/app.py +3 -2
- synth_ai/environments/service/core_routes.py +104 -110
- synth_ai/environments/service/external_registry.py +1 -2
- synth_ai/environments/service/registry.py +1 -1
- synth_ai/environments/stateful/core.py +1 -2
- synth_ai/environments/stateful/engine.py +1 -1
- synth_ai/environments/tasks/api.py +4 -4
- synth_ai/environments/tasks/core.py +14 -12
- synth_ai/environments/tasks/filters.py +6 -4
- synth_ai/environments/tasks/utils.py +13 -11
- synth_ai/evals/base.py +2 -3
- synth_ai/experimental/synth_oss.py +4 -4
- synth_ai/http.py +102 -0
- synth_ai/inference/__init__.py +7 -0
- synth_ai/inference/client.py +20 -0
- synth_ai/jobs/client.py +246 -0
- synth_ai/learning/__init__.py +24 -0
- synth_ai/learning/client.py +149 -0
- synth_ai/learning/config.py +43 -0
- synth_ai/learning/constants.py +29 -0
- synth_ai/learning/ft_client.py +59 -0
- synth_ai/learning/gateway.py +1 -3
- synth_ai/learning/health.py +43 -0
- synth_ai/learning/jobs.py +205 -0
- synth_ai/learning/prompts/banking77_injection_eval.py +15 -10
- synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +26 -14
- synth_ai/learning/prompts/mipro.py +61 -52
- synth_ai/learning/prompts/random_search.py +42 -43
- synth_ai/learning/prompts/run_mipro_banking77.py +32 -20
- synth_ai/learning/prompts/run_random_search_banking77.py +71 -52
- synth_ai/learning/rl_client.py +256 -0
- synth_ai/learning/sse.py +58 -0
- synth_ai/learning/validators.py +48 -0
- synth_ai/lm/__init__.py +5 -5
- synth_ai/lm/caching/ephemeral.py +9 -9
- synth_ai/lm/caching/handler.py +20 -20
- synth_ai/lm/caching/persistent.py +10 -10
- synth_ai/lm/config.py +3 -3
- synth_ai/lm/constants.py +7 -7
- synth_ai/lm/core/all.py +17 -3
- synth_ai/lm/core/exceptions.py +0 -2
- synth_ai/lm/core/main.py +26 -41
- synth_ai/lm/core/main_v3.py +33 -10
- synth_ai/lm/core/synth_models.py +48 -0
- synth_ai/lm/core/vendor_clients.py +26 -22
- synth_ai/lm/injection.py +7 -8
- synth_ai/lm/overrides.py +21 -19
- synth_ai/lm/provider_support/__init__.py +1 -1
- synth_ai/lm/provider_support/anthropic.py +15 -15
- synth_ai/lm/provider_support/openai.py +23 -21
- synth_ai/lm/structured_outputs/handler.py +34 -32
- synth_ai/lm/structured_outputs/inject.py +24 -27
- synth_ai/lm/structured_outputs/rehabilitate.py +19 -15
- synth_ai/lm/tools/base.py +17 -16
- synth_ai/lm/unified_interface.py +17 -18
- synth_ai/lm/vendors/base.py +20 -18
- synth_ai/lm/vendors/core/anthropic_api.py +36 -27
- synth_ai/lm/vendors/core/gemini_api.py +31 -36
- synth_ai/lm/vendors/core/mistral_api.py +19 -19
- synth_ai/lm/vendors/core/openai_api.py +42 -13
- synth_ai/lm/vendors/openai_standard.py +158 -101
- synth_ai/lm/vendors/openai_standard_responses.py +74 -61
- synth_ai/lm/vendors/retries.py +9 -1
- synth_ai/lm/vendors/supported/custom_endpoint.py +38 -28
- synth_ai/lm/vendors/supported/deepseek.py +10 -10
- synth_ai/lm/vendors/supported/grok.py +8 -8
- synth_ai/lm/vendors/supported/ollama.py +2 -1
- synth_ai/lm/vendors/supported/openrouter.py +11 -9
- synth_ai/lm/vendors/synth_client.py +425 -75
- synth_ai/lm/warmup.py +8 -7
- synth_ai/rl/__init__.py +30 -0
- synth_ai/rl/contracts.py +32 -0
- synth_ai/rl/env_keys.py +137 -0
- synth_ai/rl/secrets.py +19 -0
- synth_ai/scripts/verify_rewards.py +100 -0
- synth_ai/task/__init__.py +10 -0
- synth_ai/task/contracts.py +120 -0
- synth_ai/task/health.py +28 -0
- synth_ai/task/validators.py +12 -0
- synth_ai/tracing/__init__.py +22 -10
- synth_ai/tracing_v1/__init__.py +22 -20
- synth_ai/tracing_v3/__init__.py +7 -7
- synth_ai/tracing_v3/abstractions.py +56 -52
- synth_ai/tracing_v3/config.py +4 -2
- synth_ai/tracing_v3/db_config.py +6 -8
- synth_ai/tracing_v3/decorators.py +29 -30
- synth_ai/tracing_v3/examples/basic_usage.py +12 -12
- synth_ai/tracing_v3/hooks.py +24 -22
- synth_ai/tracing_v3/llm_call_record_helpers.py +85 -98
- synth_ai/tracing_v3/lm_call_record_abstractions.py +2 -4
- synth_ai/tracing_v3/migration_helper.py +3 -5
- synth_ai/tracing_v3/replica_sync.py +30 -32
- synth_ai/tracing_v3/session_tracer.py +158 -31
- synth_ai/tracing_v3/storage/__init__.py +1 -1
- synth_ai/tracing_v3/storage/base.py +8 -7
- synth_ai/tracing_v3/storage/config.py +4 -4
- synth_ai/tracing_v3/storage/factory.py +4 -4
- synth_ai/tracing_v3/storage/utils.py +9 -9
- synth_ai/tracing_v3/turso/__init__.py +3 -3
- synth_ai/tracing_v3/turso/daemon.py +9 -9
- synth_ai/tracing_v3/turso/manager.py +278 -48
- synth_ai/tracing_v3/turso/models.py +77 -19
- synth_ai/tracing_v3/utils.py +5 -5
- synth_ai/v0/tracing/abstractions.py +28 -28
- synth_ai/v0/tracing/base_client.py +9 -9
- synth_ai/v0/tracing/client_manager.py +7 -7
- synth_ai/v0/tracing/config.py +7 -7
- synth_ai/v0/tracing/context.py +6 -6
- synth_ai/v0/tracing/decorators.py +6 -5
- synth_ai/v0/tracing/events/manage.py +1 -1
- synth_ai/v0/tracing/events/store.py +5 -4
- synth_ai/v0/tracing/immediate_client.py +4 -5
- synth_ai/v0/tracing/local.py +3 -3
- synth_ai/v0/tracing/log_client_base.py +4 -5
- synth_ai/v0/tracing/retry_queue.py +5 -6
- synth_ai/v0/tracing/trackers.py +25 -25
- synth_ai/v0/tracing/upload.py +6 -0
- synth_ai/v0/tracing_v1/__init__.py +1 -1
- synth_ai/v0/tracing_v1/abstractions.py +28 -28
- synth_ai/v0/tracing_v1/base_client.py +9 -9
- synth_ai/v0/tracing_v1/client_manager.py +7 -7
- synth_ai/v0/tracing_v1/config.py +7 -7
- synth_ai/v0/tracing_v1/context.py +6 -6
- synth_ai/v0/tracing_v1/decorators.py +7 -6
- synth_ai/v0/tracing_v1/events/manage.py +1 -1
- synth_ai/v0/tracing_v1/events/store.py +5 -4
- synth_ai/v0/tracing_v1/immediate_client.py +4 -5
- synth_ai/v0/tracing_v1/local.py +3 -3
- synth_ai/v0/tracing_v1/log_client_base.py +4 -5
- synth_ai/v0/tracing_v1/retry_queue.py +5 -6
- synth_ai/v0/tracing_v1/trackers.py +25 -25
- synth_ai/v0/tracing_v1/upload.py +25 -24
- synth_ai/zyk/__init__.py +1 -0
- synth_ai-0.2.4.dev8.dist-info/METADATA +635 -0
- synth_ai-0.2.4.dev8.dist-info/RECORD +317 -0
- synth_ai/tui/__init__.py +0 -1
- synth_ai/tui/__main__.py +0 -13
- synth_ai/tui/cli/__init__.py +0 -1
- synth_ai/tui/cli/query_experiments.py +0 -165
- synth_ai/tui/cli/query_experiments_v3.py +0 -165
- synth_ai/tui/dashboard.py +0 -329
- synth_ai-0.2.4.dev6.dist-info/METADATA +0 -203
- synth_ai-0.2.4.dev6.dist-info/RECORD +0 -299
- {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev8.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev8.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev8.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev8.dist-info}/top_level.txt +0 -0
synth_ai/lm/warmup.py
CHANGED
@@ -3,13 +3,14 @@ Model warmup utilities for Synth backend.
|
|
3
3
|
Handles model preloading and warmup polling.
|
4
4
|
"""
|
5
5
|
|
6
|
-
import httpx
|
7
6
|
import asyncio
|
8
7
|
import logging
|
9
8
|
import sys
|
10
9
|
import time
|
11
|
-
from typing import Optional, Dict, Any
|
12
10
|
from datetime import datetime, timedelta
|
11
|
+
|
12
|
+
import httpx
|
13
|
+
|
13
14
|
from .config import SynthConfig
|
14
15
|
|
15
16
|
logger = logging.getLogger(__name__)
|
@@ -19,7 +20,7 @@ class WarmupStatus:
|
|
19
20
|
"""Track warmup status for models with TTL."""
|
20
21
|
|
21
22
|
def __init__(self):
|
22
|
-
self._warmed_models:
|
23
|
+
self._warmed_models: dict[str, datetime] = {}
|
23
24
|
self._ttl = timedelta(minutes=10) # Consider models warm for 10 minutes
|
24
25
|
|
25
26
|
def is_warm(self, model_name: str) -> bool:
|
@@ -47,11 +48,11 @@ _warmup_status = WarmupStatus()
|
|
47
48
|
|
48
49
|
async def warmup_synth_model(
|
49
50
|
model_name: str,
|
50
|
-
config:
|
51
|
-
max_attempts:
|
51
|
+
config: SynthConfig | None = None,
|
52
|
+
max_attempts: int | None = None,
|
52
53
|
force: bool = False,
|
53
54
|
verbose: bool = True,
|
54
|
-
gpu_preference:
|
55
|
+
gpu_preference: str | None = None,
|
55
56
|
) -> bool:
|
56
57
|
"""
|
57
58
|
Warm up a model on the Synth backend using fire-and-forget approach.
|
@@ -161,7 +162,7 @@ async def warmup_synth_model(
|
|
161
162
|
)
|
162
163
|
sys.stdout.flush()
|
163
164
|
await asyncio.sleep(1.0)
|
164
|
-
except Exception
|
165
|
+
except Exception:
|
165
166
|
# Continue polling; update spinner line with error label
|
166
167
|
elapsed = int(time.time() - start_time)
|
167
168
|
wheel = spinner[spin_idx % len(spinner)]
|
synth_ai/rl/__init__.py
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
from .contracts import (
|
2
|
+
RolloutEnvSpec,
|
3
|
+
RolloutPolicySpec,
|
4
|
+
RolloutRecordConfig,
|
5
|
+
RolloutSafetyConfig,
|
6
|
+
RolloutRequest,
|
7
|
+
RolloutStep,
|
8
|
+
RolloutTrajectory,
|
9
|
+
RolloutMetrics,
|
10
|
+
RolloutResponse,
|
11
|
+
)
|
12
|
+
from .env_keys import MAX_ENVIRONMENT_API_KEY_BYTES, encrypt_for_backend, setup_environment_api_key
|
13
|
+
from .secrets import mint_environment_api_key
|
14
|
+
|
15
|
+
__all__ = [
|
16
|
+
"RolloutEnvSpec",
|
17
|
+
"RolloutPolicySpec",
|
18
|
+
"RolloutRecordConfig",
|
19
|
+
"RolloutSafetyConfig",
|
20
|
+
"RolloutRequest",
|
21
|
+
"RolloutStep",
|
22
|
+
"RolloutTrajectory",
|
23
|
+
"RolloutMetrics",
|
24
|
+
"RolloutResponse",
|
25
|
+
"encrypt_for_backend",
|
26
|
+
"setup_environment_api_key",
|
27
|
+
"mint_environment_api_key",
|
28
|
+
"MAX_ENVIRONMENT_API_KEY_BYTES",
|
29
|
+
]
|
30
|
+
|
synth_ai/rl/contracts.py
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
"""
|
4
|
+
Compatibility layer: re-export Task App rollout contracts from synth_ai.task.contracts
|
5
|
+
so existing imports continue to work while consolidating under synth_ai.task.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from synth_ai.task.contracts import (
|
9
|
+
RolloutEnvSpec,
|
10
|
+
RolloutPolicySpec,
|
11
|
+
RolloutRecordConfig,
|
12
|
+
RolloutSafetyConfig,
|
13
|
+
RolloutRequest,
|
14
|
+
RolloutStep,
|
15
|
+
RolloutTrajectory,
|
16
|
+
RolloutMetrics,
|
17
|
+
RolloutResponse,
|
18
|
+
)
|
19
|
+
|
20
|
+
__all__ = [
|
21
|
+
"RolloutEnvSpec",
|
22
|
+
"RolloutPolicySpec",
|
23
|
+
"RolloutRecordConfig",
|
24
|
+
"RolloutSafetyConfig",
|
25
|
+
"RolloutRequest",
|
26
|
+
"RolloutStep",
|
27
|
+
"RolloutTrajectory",
|
28
|
+
"RolloutMetrics",
|
29
|
+
"RolloutResponse",
|
30
|
+
]
|
31
|
+
|
32
|
+
|
synth_ai/rl/env_keys.py
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
"""Helpers for uploading RL environment credentials to the backend."""
|
4
|
+
|
5
|
+
import base64
|
6
|
+
import binascii
|
7
|
+
import json
|
8
|
+
from typing import Any, Dict
|
9
|
+
import os
|
10
|
+
|
11
|
+
import requests
|
12
|
+
from nacl.public import PublicKey, SealedBox
|
13
|
+
|
14
|
+
__all__ = ["encrypt_for_backend", "setup_environment_api_key", "MAX_ENVIRONMENT_API_KEY_BYTES"]
|
15
|
+
|
16
|
+
MAX_ENVIRONMENT_API_KEY_BYTES = 8 * 1024
|
17
|
+
_ALGORITHM = "libsodium.sealedbox.v1"
|
18
|
+
|
19
|
+
|
20
|
+
def encrypt_for_backend(pubkey_b64: str, secret: str | bytes) -> str:
|
21
|
+
"""Encrypt ``secret`` for storage by the backend using libsodium sealed boxes."""
|
22
|
+
|
23
|
+
if not isinstance(pubkey_b64, str) or not pubkey_b64.strip():
|
24
|
+
raise ValueError("public key must be a non-empty base64 string")
|
25
|
+
|
26
|
+
try:
|
27
|
+
key_bytes = base64.b64decode(pubkey_b64, validate=True)
|
28
|
+
except binascii.Error as exc: # pragma: no cover - defensive guard
|
29
|
+
raise ValueError("public key must be base64-encoded") from exc
|
30
|
+
|
31
|
+
if len(key_bytes) != 32:
|
32
|
+
raise ValueError("public key must be 32 bytes for X25519")
|
33
|
+
|
34
|
+
if isinstance(secret, str):
|
35
|
+
secret_bytes = secret.encode("utf-8")
|
36
|
+
elif isinstance(secret, bytes):
|
37
|
+
secret_bytes = secret
|
38
|
+
else: # pragma: no cover - type guard
|
39
|
+
raise TypeError("secret must be str or bytes")
|
40
|
+
|
41
|
+
if not secret_bytes:
|
42
|
+
raise ValueError("secret must not be empty")
|
43
|
+
|
44
|
+
box = SealedBox(PublicKey(key_bytes))
|
45
|
+
ciphertext = box.encrypt(secret_bytes)
|
46
|
+
return base64.b64encode(ciphertext).decode("ascii")
|
47
|
+
|
48
|
+
|
49
|
+
def setup_environment_api_key(
|
50
|
+
backend_base: str,
|
51
|
+
synth_api_key: str,
|
52
|
+
token: str | None = None,
|
53
|
+
*,
|
54
|
+
timeout: float = 15.0,
|
55
|
+
) -> Dict[str, Any]:
|
56
|
+
"""Upload an ENVIRONMENT_API_KEY to the backend."""
|
57
|
+
|
58
|
+
backend = backend_base.rstrip("/")
|
59
|
+
if not backend:
|
60
|
+
raise ValueError("backend_base must be provided")
|
61
|
+
if not synth_api_key:
|
62
|
+
raise ValueError("synth_api_key must be provided")
|
63
|
+
|
64
|
+
# Require caller-provided plaintext. If not provided, read from ENVIRONMENT_API_KEY.
|
65
|
+
plaintext = token if token is not None else os.getenv("ENVIRONMENT_API_KEY", "").strip()
|
66
|
+
if not plaintext:
|
67
|
+
raise ValueError("ENVIRONMENT_API_KEY must be set (or pass token=...) to upload")
|
68
|
+
if not isinstance(plaintext, str): # pragma: no cover - defensive guard
|
69
|
+
raise TypeError("token must be a string")
|
70
|
+
|
71
|
+
token_bytes = plaintext.encode("utf-8")
|
72
|
+
if not token_bytes:
|
73
|
+
raise ValueError("ENVIRONMENT_API_KEY token must not be empty")
|
74
|
+
if len(token_bytes) > MAX_ENVIRONMENT_API_KEY_BYTES:
|
75
|
+
raise ValueError("ENVIRONMENT_API_KEY token exceeds 8 KiB limit")
|
76
|
+
|
77
|
+
headers = {"Authorization": f"Bearer {synth_api_key}"}
|
78
|
+
pub_url = f"{backend}/api/v1/crypto/public-key"
|
79
|
+
response = requests.get(pub_url, headers=headers, timeout=timeout)
|
80
|
+
_raise_with_detail(response)
|
81
|
+
|
82
|
+
try:
|
83
|
+
doc = response.json()
|
84
|
+
except ValueError as exc: # pragma: no cover - backend invariant
|
85
|
+
raise RuntimeError("backend returned invalid JSON for public key") from exc
|
86
|
+
|
87
|
+
if not isinstance(doc, dict):
|
88
|
+
raise RuntimeError("backend public key response must be an object")
|
89
|
+
|
90
|
+
pubkey = doc.get("public_key")
|
91
|
+
if not isinstance(pubkey, str) or not pubkey:
|
92
|
+
raise RuntimeError("backend response missing public_key")
|
93
|
+
|
94
|
+
# The backend currently returns a single algorithm identifier; keep a guard in
|
95
|
+
# case future versions change the value and we need to surface that to callers.
|
96
|
+
alg = doc.get("alg")
|
97
|
+
if alg is not None and alg != _ALGORITHM:
|
98
|
+
raise RuntimeError(f"unsupported sealed box algorithm: {alg}")
|
99
|
+
|
100
|
+
ciphertext_b64 = encrypt_for_backend(pubkey, token_bytes)
|
101
|
+
|
102
|
+
body = {"name": "ENVIRONMENT_API_KEY", "ciphertext_b64": ciphertext_b64}
|
103
|
+
post_url = f"{backend}/api/v1/env-keys"
|
104
|
+
response2 = requests.post(post_url, headers={**headers, "Content-Type": "application/json"}, json=body, timeout=timeout)
|
105
|
+
_raise_with_detail(response2)
|
106
|
+
|
107
|
+
try:
|
108
|
+
upload_doc = response2.json()
|
109
|
+
except ValueError:
|
110
|
+
upload_doc = {}
|
111
|
+
|
112
|
+
if not isinstance(upload_doc, dict):
|
113
|
+
upload_doc = {}
|
114
|
+
|
115
|
+
return {
|
116
|
+
"stored": True,
|
117
|
+
"id": upload_doc.get("id"),
|
118
|
+
"name": upload_doc.get("name"),
|
119
|
+
"updated_at": upload_doc.get("updated_at"),
|
120
|
+
}
|
121
|
+
|
122
|
+
|
123
|
+
def _raise_with_detail(response: requests.Response) -> None:
|
124
|
+
try:
|
125
|
+
response.raise_for_status()
|
126
|
+
except requests.HTTPError as exc:
|
127
|
+
detail_snippet: str | None = None
|
128
|
+
try:
|
129
|
+
detail = response.json()
|
130
|
+
detail_snippet = json.dumps(detail, separators=(",", ":"))[:200]
|
131
|
+
except Exception:
|
132
|
+
body = response.text if response.text is not None else ""
|
133
|
+
detail_snippet = body[:200] if body else None
|
134
|
+
message = str(exc)
|
135
|
+
if detail_snippet:
|
136
|
+
message = f"{message} | body={detail_snippet}"
|
137
|
+
raise requests.HTTPError(message, request=exc.request, response=exc.response) from None
|
synth_ai/rl/secrets.py
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
"""Helpers for generating RL environment credentials."""
|
4
|
+
|
5
|
+
import secrets
|
6
|
+
|
7
|
+
__all__ = ["mint_environment_api_key"]
|
8
|
+
|
9
|
+
|
10
|
+
def mint_environment_api_key() -> str:
|
11
|
+
"""Mint a random ENVIRONMENT_API_KEY value.
|
12
|
+
|
13
|
+
The current format is 64 hexadecimal characters (256 bits of entropy), which
|
14
|
+
matches the shell helpers used by the RL examples. This keeps the token easy
|
15
|
+
to copy while remaining suitably strong for authentication.
|
16
|
+
"""
|
17
|
+
|
18
|
+
# secrets.token_hex(32) → 32 random bytes rendered as 64 hex characters.
|
19
|
+
return secrets.token_hex(32)
|
@@ -0,0 +1,100 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Verify reward persistence in a traces database.
|
4
|
+
|
5
|
+
Usage:
|
6
|
+
uv run python -m synth_ai.scripts.verify_rewards --db /path/to/db.sqlite --min-reward 1
|
7
|
+
"""
|
8
|
+
|
9
|
+
import argparse
|
10
|
+
import asyncio
|
11
|
+
import os
|
12
|
+
from typing import Dict
|
13
|
+
|
14
|
+
from sqlalchemy import text
|
15
|
+
|
16
|
+
from synth_ai.tracing_v3.turso.manager import AsyncSQLTraceManager
|
17
|
+
|
18
|
+
|
19
|
+
async def verify(db_path: str, min_reward: int) -> int:
|
20
|
+
db_url = db_path
|
21
|
+
if not db_url.startswith("sqlite+aiosqlite:///"):
|
22
|
+
db_url = f"sqlite+aiosqlite:///{os.path.abspath(db_path)}"
|
23
|
+
|
24
|
+
mgr = AsyncSQLTraceManager(db_url=db_url)
|
25
|
+
await mgr.initialize()
|
26
|
+
|
27
|
+
try:
|
28
|
+
async with mgr.session() as session:
|
29
|
+
# Sessions with outcome_rewards
|
30
|
+
q_good = text(
|
31
|
+
"""
|
32
|
+
SELECT session_id, MAX(total_reward) as total_reward
|
33
|
+
FROM outcome_rewards
|
34
|
+
GROUP BY session_id
|
35
|
+
"""
|
36
|
+
)
|
37
|
+
res = await session.execute(q_good)
|
38
|
+
outcomes = {row[0]: int(row[1]) for row in res.fetchall()}
|
39
|
+
|
40
|
+
# Sessions without outcome_rewards
|
41
|
+
q_missing = text(
|
42
|
+
"""
|
43
|
+
SELECT s.session_id
|
44
|
+
FROM session_traces s
|
45
|
+
LEFT JOIN outcome_rewards o ON s.session_id = o.session_id
|
46
|
+
WHERE o.session_id IS NULL
|
47
|
+
"""
|
48
|
+
)
|
49
|
+
res2 = await session.execute(q_missing)
|
50
|
+
missing = [row[0] for row in res2.fetchall()]
|
51
|
+
|
52
|
+
# Aggregate event_rewards per session (informational)
|
53
|
+
q_event = text(
|
54
|
+
"""
|
55
|
+
SELECT session_id, COALESCE(SUM(reward_value), 0.0) as sum_rewards
|
56
|
+
FROM event_rewards
|
57
|
+
GROUP BY session_id
|
58
|
+
"""
|
59
|
+
)
|
60
|
+
res3 = await session.execute(q_event)
|
61
|
+
event_sums: Dict[str, float] = {row[0]: float(row[1]) for row in res3.fetchall()}
|
62
|
+
|
63
|
+
print(f"Sessions with outcome_rewards: {len(outcomes)}")
|
64
|
+
print(f"Sessions missing outcome_rewards: {len(missing)}")
|
65
|
+
if missing:
|
66
|
+
print("Missing session_ids:", ", ".join(missing[:10]) + (" ..." if len(missing) > 10 else ""))
|
67
|
+
|
68
|
+
# Threshold check
|
69
|
+
qualifying = {sid: r for sid, r in outcomes.items() if r >= min_reward}
|
70
|
+
print(f"Sessions with total_reward >= {min_reward}: {len(qualifying)}")
|
71
|
+
|
72
|
+
# Show a small comparison snapshot
|
73
|
+
sample = list(qualifying.items())[:5]
|
74
|
+
for sid, tot in sample:
|
75
|
+
er = event_sums.get(sid, 0.0)
|
76
|
+
print(f" {sid}: outcome={tot}, sum(event_rewards)={er:.2f}")
|
77
|
+
|
78
|
+
# Exit non-zero if any sessions are missing outcome rewards
|
79
|
+
if missing:
|
80
|
+
return 2
|
81
|
+
if min_reward > 0 and not qualifying:
|
82
|
+
return 3
|
83
|
+
return 0
|
84
|
+
finally:
|
85
|
+
await mgr.close()
|
86
|
+
|
87
|
+
|
88
|
+
def main() -> int:
|
89
|
+
ap = argparse.ArgumentParser(description="Verify reward persistence in traces DB")
|
90
|
+
ap.add_argument("--db", required=True, help="Path to traces SQLite DB (aiosqlite)")
|
91
|
+
ap.add_argument("--min-reward", type=int, default=0, help="Minimum total_reward to consider qualifying")
|
92
|
+
args = ap.parse_args()
|
93
|
+
|
94
|
+
return asyncio.run(verify(args.db, args.min_reward))
|
95
|
+
|
96
|
+
|
97
|
+
if __name__ == "__main__":
|
98
|
+
raise SystemExit(main())
|
99
|
+
|
100
|
+
|
@@ -0,0 +1,120 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from typing import Optional, Any, Dict, List
|
5
|
+
from pydantic import BaseModel
|
6
|
+
|
7
|
+
|
8
|
+
@dataclass(frozen=True)
|
9
|
+
class TaskAppEndpoints:
|
10
|
+
"""Canonical Task App endpoint shapes used by RL trainers.
|
11
|
+
|
12
|
+
The Task App is an HTTP service (often deployed on Modal) that exposes:
|
13
|
+
- Health: GET /health
|
14
|
+
• Requires header X-API-Key (when ENVIRONMENT_API_KEY is configured)
|
15
|
+
• Returns { healthy: true }
|
16
|
+
- Environment lifecycle:
|
17
|
+
• POST /env/{env_name}/initialize → { env_id, observation }
|
18
|
+
• POST /env/{env_name}/step → { observation, reward, done, info }
|
19
|
+
• POST /env/{env_name}/terminate → { ok: true }
|
20
|
+
- Rollout (optional, unified schema):
|
21
|
+
• POST /rollout → { run_id, trajectories[], metrics, ... }
|
22
|
+
- Proxy (optional):
|
23
|
+
• POST /proxy/v1/chat/completions (for direct OpenAI calls from Task App)
|
24
|
+
"""
|
25
|
+
|
26
|
+
health: str = "/health"
|
27
|
+
rollout: str = "/rollout"
|
28
|
+
proxy_chat_completions: str = "/proxy/v1/chat/completions"
|
29
|
+
env_initialize: str = "/env/{env_name}/initialize"
|
30
|
+
env_step: str = "/env/{env_name}/step"
|
31
|
+
env_terminate: str = "/env/{env_name}/terminate"
|
32
|
+
|
33
|
+
|
34
|
+
@dataclass(frozen=True)
|
35
|
+
class TaskAppContract:
|
36
|
+
"""Requirements and expectations for a Task App used by RL trainers.
|
37
|
+
|
38
|
+
- Auth: ENVIRONMENT_API_KEY must be set in the Task App environment; requests include X-API-Key.
|
39
|
+
- Health: /health returns 200 and JSON; may verify X-API-Key header.
|
40
|
+
- Env API: initialize/step/terminate are present for the target env (e.g., CrafterClassic).
|
41
|
+
- Rollout API: optional; provides a single-call rollout for convenience/testing.
|
42
|
+
- Inference routing: policy config passes an inference_url (Synth backend or OpenAI proxy).
|
43
|
+
- URL: base must be reachable via HTTPS and should be under .modal.run in production.
|
44
|
+
"""
|
45
|
+
|
46
|
+
base_url: str
|
47
|
+
env_name: Optional[str] = None
|
48
|
+
requires_api_key_header: bool = True
|
49
|
+
|
50
|
+
|
51
|
+
# --- Unified rollout schema used by Task App services and SDK utilities ---
|
52
|
+
|
53
|
+
class RolloutEnvSpec(BaseModel):
|
54
|
+
env_id: Optional[str] = None
|
55
|
+
env_name: Optional[str] = None
|
56
|
+
config: Dict[str, Any] = {}
|
57
|
+
seed: Optional[int] = None
|
58
|
+
|
59
|
+
|
60
|
+
class RolloutPolicySpec(BaseModel):
|
61
|
+
policy_id: Optional[str] = None
|
62
|
+
policy_name: Optional[str] = None
|
63
|
+
config: Dict[str, Any] = {}
|
64
|
+
|
65
|
+
|
66
|
+
class RolloutRecordConfig(BaseModel):
|
67
|
+
trajectories: bool = True
|
68
|
+
logprobs: bool = False
|
69
|
+
value: bool = False
|
70
|
+
|
71
|
+
|
72
|
+
class RolloutSafetyConfig(BaseModel):
|
73
|
+
max_ops: int = 100000
|
74
|
+
max_time_s: float = 3600.0
|
75
|
+
|
76
|
+
|
77
|
+
class RolloutRequest(BaseModel):
|
78
|
+
run_id: str
|
79
|
+
env: RolloutEnvSpec
|
80
|
+
policy: RolloutPolicySpec
|
81
|
+
ops: List[Dict[str, Any]] | List[str]
|
82
|
+
record: RolloutRecordConfig = RolloutRecordConfig()
|
83
|
+
on_done: str = "reset"
|
84
|
+
safety: RolloutSafetyConfig = RolloutSafetyConfig()
|
85
|
+
training_session_id: Optional[str] = None
|
86
|
+
synth_base_url: Optional[str] = None
|
87
|
+
|
88
|
+
|
89
|
+
class RolloutStep(BaseModel):
|
90
|
+
obs: Dict[str, Any]
|
91
|
+
tool_calls: List[Dict[str, Any]]
|
92
|
+
reward: Optional[float] = None
|
93
|
+
done: bool = False
|
94
|
+
truncated: Optional[bool] = None
|
95
|
+
info: Optional[Dict[str, Any]] = None
|
96
|
+
|
97
|
+
|
98
|
+
class RolloutTrajectory(BaseModel):
|
99
|
+
env_id: str
|
100
|
+
policy_id: str
|
101
|
+
steps: List[RolloutStep]
|
102
|
+
final: Optional[Dict[str, Any]] = None
|
103
|
+
length: int
|
104
|
+
|
105
|
+
|
106
|
+
class RolloutMetrics(BaseModel):
|
107
|
+
episode_returns: List[float]
|
108
|
+
mean_return: float
|
109
|
+
num_steps: int
|
110
|
+
num_episodes: int = 0
|
111
|
+
|
112
|
+
|
113
|
+
class RolloutResponse(BaseModel):
|
114
|
+
run_id: str
|
115
|
+
trajectories: List[RolloutTrajectory]
|
116
|
+
branches: Dict[str, List[str]] = {}
|
117
|
+
metrics: RolloutMetrics
|
118
|
+
aborted: bool = False
|
119
|
+
ops_executed: int = 0
|
120
|
+
|
synth_ai/task/health.py
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import Any, Dict
|
4
|
+
import aiohttp
|
5
|
+
|
6
|
+
|
7
|
+
async def task_app_health(task_app_url: str) -> Dict[str, Any]:
|
8
|
+
"""Probe a Task App base URL for basic reachability.
|
9
|
+
|
10
|
+
Behavior:
|
11
|
+
- Try HEAD first (follows redirects)
|
12
|
+
- Fallback to GET if HEAD is unsupported
|
13
|
+
- Returns {ok: bool, status?: int, error?: str}
|
14
|
+
"""
|
15
|
+
try:
|
16
|
+
async with aiohttp.ClientSession() as session:
|
17
|
+
async with session.head(task_app_url, allow_redirects=True) as r:
|
18
|
+
if 200 <= r.status < 400:
|
19
|
+
return {"ok": True, "status": r.status}
|
20
|
+
async with aiohttp.ClientSession() as session:
|
21
|
+
async with session.get(task_app_url, allow_redirects=True) as r2:
|
22
|
+
if 200 <= r2.status < 400:
|
23
|
+
return {"ok": True, "status": r2.status}
|
24
|
+
return {"ok": False, "status": None}
|
25
|
+
except Exception as e:
|
26
|
+
return {"ok": False, "error": f"{type(e).__name__}: {e}"}
|
27
|
+
|
28
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from urllib.parse import urlparse
|
4
|
+
|
5
|
+
|
6
|
+
def validate_task_app_url(url: str, *, name: str = "TASK_APP_BASE_URL") -> None:
|
7
|
+
"""Validate a Task App base URL (scheme + host present)."""
|
8
|
+
|
9
|
+
p = urlparse(url)
|
10
|
+
if p.scheme not in ("http", "https") or not p.netloc:
|
11
|
+
raise ValueError(f"Invalid {name}: malformed: {url}")
|
12
|
+
|
synth_ai/tracing/__init__.py
CHANGED
@@ -1,18 +1,30 @@
|
|
1
|
-
import sys as _sys
|
2
1
|
import importlib as _importlib
|
2
|
+
import sys as _sys
|
3
3
|
|
4
|
-
_pkg = _importlib.import_module(
|
4
|
+
_pkg = _importlib.import_module("synth_ai.v0.tracing")
|
5
5
|
_sys.modules[__name__] = _pkg
|
6
6
|
|
7
7
|
_SUBMODULES = [
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
"abstractions",
|
9
|
+
"base_client",
|
10
|
+
"client_manager",
|
11
|
+
"config",
|
12
|
+
"context",
|
13
|
+
"decorators",
|
14
|
+
"immediate_client",
|
15
|
+
"local",
|
16
|
+
"log_client_base",
|
17
|
+
"retry_queue",
|
18
|
+
"trackers",
|
19
|
+
"upload",
|
20
|
+
"utils",
|
11
21
|
]
|
12
22
|
for _m in _SUBMODULES:
|
13
|
-
_sys.modules[f
|
23
|
+
_sys.modules[f"{__name__}.{_m}"] = _importlib.import_module(f"synth_ai.v0.tracing.{_m}")
|
14
24
|
|
15
|
-
_events_pkg = _importlib.import_module(
|
16
|
-
_sys.modules[f
|
17
|
-
for _m in [
|
18
|
-
_sys.modules[f
|
25
|
+
_events_pkg = _importlib.import_module("synth_ai.v0.tracing.events")
|
26
|
+
_sys.modules[f"{__name__}.events"] = _events_pkg
|
27
|
+
for _m in ["manage", "scope", "store"]:
|
28
|
+
_sys.modules[f"{__name__}.events.{_m}"] = _importlib.import_module(
|
29
|
+
f"synth_ai.v0.tracing.events.{_m}"
|
30
|
+
)
|
synth_ai/tracing_v1/__init__.py
CHANGED
@@ -1,31 +1,33 @@
|
|
1
|
-
import sys as _sys
|
2
1
|
import importlib as _importlib
|
2
|
+
import sys as _sys
|
3
3
|
|
4
4
|
# Forward top-level package
|
5
|
-
_pkg = _importlib.import_module(
|
5
|
+
_pkg = _importlib.import_module("synth_ai.v0.tracing_v1")
|
6
6
|
_sys.modules[__name__] = _pkg
|
7
7
|
|
8
8
|
# Explicitly forward submodules so `synth_ai.tracing_v1.X` works
|
9
9
|
_SUBMODULES = [
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
10
|
+
"abstractions",
|
11
|
+
"base_client",
|
12
|
+
"client_manager",
|
13
|
+
"config",
|
14
|
+
"context",
|
15
|
+
"decorators",
|
16
|
+
"immediate_client",
|
17
|
+
"local",
|
18
|
+
"log_client_base",
|
19
|
+
"retry_queue",
|
20
|
+
"trackers",
|
21
|
+
"upload",
|
22
|
+
"utils",
|
23
23
|
]
|
24
24
|
for _m in _SUBMODULES:
|
25
|
-
_sys.modules[f
|
25
|
+
_sys.modules[f"{__name__}.{_m}"] = _importlib.import_module(f"synth_ai.v0.tracing_v1.{_m}")
|
26
26
|
|
27
27
|
# Forward events package and its submodules
|
28
|
-
_events_pkg = _importlib.import_module(
|
29
|
-
_sys.modules[f
|
30
|
-
for _m in [
|
31
|
-
_sys.modules[f
|
28
|
+
_events_pkg = _importlib.import_module("synth_ai.v0.tracing_v1.events")
|
29
|
+
_sys.modules[f"{__name__}.events"] = _events_pkg
|
30
|
+
for _m in ["manage", "scope", "store"]:
|
31
|
+
_sys.modules[f"{__name__}.events.{_m}"] = _importlib.import_module(
|
32
|
+
f"synth_ai.v0.tracing_v1.events.{_m}"
|
33
|
+
)
|
synth_ai/tracing_v3/__init__.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
"""Tracing v3 - Turso/sqld based tracing implementation.
|
2
2
|
|
3
3
|
This module provides a modern, async-first tracing system for capturing and storing
|
4
|
-
detailed execution traces from AI systems. It's designed to handle high-throughput
|
4
|
+
detailed execution traces from AI systems. It's designed to handle high-throughput
|
5
5
|
scenarios with proper async/await patterns throughout.
|
6
6
|
|
7
7
|
Architecture Overview:
|
@@ -49,10 +49,10 @@ Key Features:
|
|
49
49
|
Usage Example:
|
50
50
|
-------------
|
51
51
|
from synth_ai.tracing_v3 import SessionTracer
|
52
|
-
|
52
|
+
|
53
53
|
tracer = SessionTracer()
|
54
54
|
await tracer.initialize()
|
55
|
-
|
55
|
+
|
56
56
|
async with tracer.session() as session_id:
|
57
57
|
async with tracer.timestep("step1", turn_number=1):
|
58
58
|
# Record events during execution
|
@@ -70,17 +70,17 @@ The system uses environment variables for configuration:
|
|
70
70
|
See `config.py` for full configuration options.
|
71
71
|
"""
|
72
72
|
|
73
|
-
from .session_tracer import SessionTracer
|
74
73
|
from .abstractions import (
|
75
|
-
SessionTrace,
|
76
|
-
SessionTimeStep,
|
77
74
|
BaseEvent,
|
78
|
-
RuntimeEvent,
|
79
75
|
EnvironmentEvent,
|
76
|
+
RuntimeEvent,
|
80
77
|
SessionEventMarkovBlanketMessage,
|
78
|
+
SessionTimeStep,
|
79
|
+
SessionTrace,
|
81
80
|
TimeRecord,
|
82
81
|
)
|
83
82
|
from .config import TursoConfig
|
83
|
+
from .session_tracer import SessionTracer
|
84
84
|
|
85
85
|
__all__ = [
|
86
86
|
"SessionTracer",
|