aleph-rlm 0.6.0__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.
aleph/config.py ADDED
@@ -0,0 +1,154 @@
1
+ """Configuration management for Aleph.
2
+
3
+ AlephConfig can be instantiated directly, loaded from env vars, or loaded from a
4
+ YAML/JSON config file.
5
+
6
+ The goal is to make it easy to go from *configuration* -> a ready-to-run Aleph
7
+ instance.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ import os
14
+ from collections.abc import Mapping
15
+ from dataclasses import dataclass, field
16
+ from pathlib import Path
17
+ from typing import Any, Literal, cast
18
+
19
+ from .types import Budget
20
+ from .repl.sandbox import DEFAULT_ALLOWED_IMPORTS, SandboxConfig
21
+ from .providers.registry import get_provider
22
+ from .core import Aleph
23
+
24
+
25
+ @dataclass(slots=True)
26
+ class AlephConfig:
27
+ """Complete configuration for an Aleph instance."""
28
+
29
+ # Provider / models
30
+ provider: str = "anthropic"
31
+ root_model: str = "claude-sonnet-4-20250514"
32
+ sub_model: str | None = None
33
+ api_key: str | None = None
34
+
35
+ # Budget defaults
36
+ max_tokens: int | None = None
37
+ max_iterations: int = 100
38
+ max_depth: int = 2
39
+ max_wall_time_seconds: float = 300.0
40
+ max_sub_queries: int = 100
41
+
42
+ # Sandbox
43
+ enable_code_execution: bool = True
44
+ allowed_imports: list[str] = field(default_factory=lambda: list(DEFAULT_ALLOWED_IMPORTS))
45
+ sandbox_timeout_seconds: float = 60.0
46
+ max_output_chars: int = 50_000
47
+
48
+ # REPL
49
+ context_var_name: str = "ctx"
50
+
51
+ # Caching
52
+ enable_caching: bool = True
53
+ cache_backend: Literal["memory"] = "memory"
54
+
55
+ # Observability
56
+ log_trajectory: bool = True
57
+ log_level: str = "INFO"
58
+
59
+ # Custom prompt
60
+ system_prompt: str | None = None
61
+
62
+ @classmethod
63
+ def from_file(cls, path: str | Path) -> "AlephConfig":
64
+ """Load config from YAML or JSON."""
65
+
66
+ path = Path(path)
67
+ content = path.read_text(encoding="utf-8")
68
+
69
+ if path.suffix.lower() in {".yaml", ".yml"}:
70
+ try:
71
+ import yaml
72
+ except Exception as e: # pragma: no cover
73
+ raise RuntimeError(
74
+ "YAML support requires PyYAML. Install aleph[yaml] or `pip install pyyaml`."
75
+ ) from e
76
+ data = yaml.safe_load(content) or {}
77
+ else:
78
+ data = json.loads(content) if content.strip() else {}
79
+
80
+ if not isinstance(data, dict):
81
+ raise ValueError(f"Config file must parse to an object/dict, got: {type(data)}")
82
+ return cls(**cast(dict[str, Any], data))
83
+
84
+ @classmethod
85
+ def from_env(cls) -> "AlephConfig":
86
+ """Load config from environment variables."""
87
+
88
+ def getenv_int(name: str, default: int | None) -> int | None:
89
+ v = os.getenv(name)
90
+ if v is None or v == "":
91
+ return default
92
+ return int(v)
93
+
94
+ return cls(
95
+ provider=os.getenv("ALEPH_PROVIDER", os.getenv("RLM_PROVIDER", "anthropic")),
96
+ root_model=os.getenv("ALEPH_MODEL", os.getenv("RLM_MODEL", "claude-sonnet-4-20250514")),
97
+ sub_model=os.getenv("ALEPH_SUB_MODEL", os.getenv("RLM_SUB_MODEL")),
98
+ api_key=os.getenv("ALEPH_API_KEY", os.getenv("RLM_API_KEY")),
99
+ max_tokens=getenv_int("ALEPH_MAX_TOKENS", None),
100
+ max_iterations=int(os.getenv("ALEPH_MAX_ITERATIONS", "100")),
101
+ max_depth=int(os.getenv("ALEPH_MAX_DEPTH", "2")),
102
+ max_wall_time_seconds=float(os.getenv("ALEPH_MAX_WALL_TIME", "300")),
103
+ max_sub_queries=int(os.getenv("ALEPH_MAX_SUB_QUERIES", "100")),
104
+ enable_caching=os.getenv("ALEPH_ENABLE_CACHING", "true").lower() in {"1", "true", "yes"},
105
+ log_trajectory=os.getenv("ALEPH_LOG_TRAJECTORY", "true").lower() in {"1", "true", "yes"},
106
+ )
107
+
108
+ def to_budget(self) -> Budget:
109
+ """Convert this config to a :class:`~aleph.types.Budget` instance."""
110
+ return Budget(
111
+ max_tokens=self.max_tokens,
112
+ max_iterations=self.max_iterations,
113
+ max_depth=self.max_depth,
114
+ max_wall_time_seconds=self.max_wall_time_seconds,
115
+ max_sub_queries=self.max_sub_queries,
116
+ )
117
+
118
+ def to_sandbox_config(self) -> SandboxConfig:
119
+ """Convert this config to a :class:`~aleph.repl.sandbox.SandboxConfig` instance."""
120
+ return SandboxConfig(
121
+ allowed_imports=self.allowed_imports,
122
+ max_output_chars=self.max_output_chars,
123
+ timeout_seconds=self.sandbox_timeout_seconds,
124
+ enable_code_execution=self.enable_code_execution,
125
+ )
126
+
127
+
128
+ def create_aleph(config: AlephConfig | Mapping[str, object] | str | Path | None = None) -> Aleph:
129
+ """Factory to create Aleph from config sources."""
130
+
131
+ if config is None:
132
+ cfg = AlephConfig.from_env()
133
+ elif isinstance(config, AlephConfig):
134
+ cfg = config
135
+ elif isinstance(config, Mapping):
136
+ cfg = AlephConfig(**cast(dict[str, Any], dict(config)))
137
+ elif isinstance(config, (str, Path)):
138
+ cfg = AlephConfig.from_file(config)
139
+ else:
140
+ raise TypeError(f"Invalid config type: {type(config)}")
141
+
142
+ # Provider instance
143
+ provider = get_provider(cfg.provider, api_key=cfg.api_key)
144
+
145
+ return Aleph(
146
+ provider=provider,
147
+ root_model=cfg.root_model,
148
+ sub_model=cfg.sub_model or cfg.root_model,
149
+ budget=cfg.to_budget(),
150
+ sandbox_config=cfg.to_sandbox_config(),
151
+ system_prompt=cfg.system_prompt,
152
+ enable_caching=cfg.enable_caching,
153
+ log_trajectory=cfg.log_trajectory,
154
+ )