flatagents 2.7.0__tar.gz → 4.0.0__tar.gz
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.
- {flatagents-2.7.0 → flatagents-4.0.0}/AGENTS.md +19 -4
- {flatagents-2.7.0 → flatagents-4.0.0}/PKG-INFO +5 -4
- {flatagents-2.7.0 → flatagents-4.0.0}/README.md +4 -3
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/__init__.py +34 -1
- flatagents-4.0.0/flatagents/adapters/__init__.py +32 -0
- flatagents-4.0.0/flatagents/adapters/call_throttle.py +110 -0
- flatagents-4.0.0/flatagents/adapters/claude_code.py +931 -0
- flatagents-4.0.0/flatagents/adapters/claude_code_sessions.py +295 -0
- flatagents-4.0.0/flatagents/adapters/codex_cli.py +993 -0
- flatagents-4.0.0/flatagents/adapters/codex_cli_sessions.py +291 -0
- flatagents-4.0.0/flatagents/adapters/compat.py +41 -0
- flatagents-4.0.0/flatagents/adapters/pi_agent_bridge.py +125 -0
- flatagents-4.0.0/flatagents/adapters/pi_agent_runner.mjs +99 -0
- flatagents-4.0.0/flatagents/adapters/smolagents.py +122 -0
- flatagents-4.0.0/flatagents/assets/flatagent.d.ts +56 -0
- flatagents-4.0.0/flatagents/assets/flatagent.schema.json +689 -0
- flatagents-4.0.0/flatagents/assets/flatagent.slim.d.ts +15 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/assets/flatagents-runtime.d.ts +1 -1
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/assets/flatagents-runtime.schema.json +1 -1
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/assets/flatagents-runtime.slim.d.ts +1 -1
- flatagents-4.0.0/flatagents/assets/flatmachine.d.ts +367 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/assets/flatmachine.schema.json +603 -280
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/assets/flatmachine.slim.d.ts +14 -13
- flatagents-4.0.0/flatagents/assets/profile.d.ts +138 -0
- flatagents-2.7.0/flatagents/assets/flatagent.schema.json → flatagents-4.0.0/flatagents/assets/profile.schema.json +220 -169
- flatagents-4.0.0/flatagents/assets/profile.slim.d.ts +100 -0
- flatagents-4.0.0/flatagents/assets/prompt.d.ts +81 -0
- flatagents-4.0.0/flatagents/assets/prompt.schema.json +213 -0
- flatagents-2.7.0/flatagents/assets/flatagent.slim.d.ts → flatagents-4.0.0/flatagents/assets/prompt.slim.d.ts +8 -36
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/baseagent.py +10 -6
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/flatagent.py +386 -53
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/profiles.py +81 -2
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/validation.py +10 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/pyproject.toml +1 -1
- flatagents-2.7.0/MACHINES.md +0 -207
- flatagents-2.7.0/flatagents/assets/flatagent.d.ts +0 -245
- flatagents-2.7.0/flatagents/assets/flatmachine.d.ts +0 -480
- flatagents-2.7.0/flatagents/assets/profiles.d.ts +0 -156
- flatagents-2.7.0/flatagents/assets/profiles.schema.json +0 -138
- flatagents-2.7.0/flatagents/assets/profiles.slim.d.ts +0 -39
- {flatagents-2.7.0 → flatagents-4.0.0}/.gitignore +0 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/assets/README.md +0 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/assets/__init__.py +0 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/monitoring.py +0 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/providers/__init__.py +0 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/providers/anthropic.py +0 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/providers/cerebras.py +0 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/providers/github_copilot_auth.py +0 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/providers/github_copilot_client.py +0 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/providers/github_copilot_login.py +0 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/providers/github_copilot_types.py +0 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/providers/openai.py +0 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/providers/openai_codex_auth.py +0 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/providers/openai_codex_client.py +0 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/providers/openai_codex_login.py +0 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/providers/openai_codex_types.py +0 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/tool_loop.py +0 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/tools.py +0 -0
- {flatagents-2.7.0 → flatagents-4.0.0}/flatagents/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# FlatAgents + FlatMachines Reference
|
|
2
2
|
|
|
3
|
-
> **Target: <1000 tokens.** LLM-optimized. See `flatagent.d.ts`, `flatmachine.d.ts`, `
|
|
3
|
+
> **Target: <1000 tokens.** LLM-optimized. See `flatagent.d.ts`, `flatmachine.d.ts`, `profile.d.ts`, `prompt.d.ts` for schemas.
|
|
4
4
|
>
|
|
5
5
|
> **Versioning:** All specs and SDKs use lockstep versioning.
|
|
6
6
|
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
|
|
22
22
|
```yaml
|
|
23
23
|
# profiles.yml — agents reference by name
|
|
24
|
-
spec:
|
|
25
|
-
spec_version: "
|
|
24
|
+
spec: flatprofile
|
|
25
|
+
spec_version: "4.0.0"
|
|
26
26
|
data:
|
|
27
27
|
model_profiles:
|
|
28
28
|
fast: { provider: cerebras, name: zai-glm-4.6, temperature: 0.6 }
|
|
@@ -40,7 +40,7 @@ Resolution: default → profile → overrides → override
|
|
|
40
40
|
- Backend selection precedence remains: constructor `backend` → resolved `model.backend` → auto-detect (litellm/aisuite only).
|
|
41
41
|
- `oauth` settings are read from resolved model config; works identically whether model came from inline agent config or profile.
|
|
42
42
|
- Auth file precedence:
|
|
43
|
-
- Codex: `oauth.auth_file` → legacy `codex_auth_file` → legacy `auth.auth_file` → `FLATAGENTS_CODEX_AUTH_FILE` → `~/.
|
|
43
|
+
- Codex: `oauth.auth_file` → legacy `codex_auth_file` → legacy `auth.auth_file` → `FLATAGENTS_CODEX_AUTH_FILE` → `~/.pi/agent/auth.json`
|
|
44
44
|
- Copilot: `oauth.auth_file` → `copilot_auth_file` → legacy `auth.auth_file` → `FLATAGENTS_COPILOT_AUTH_FILE` → `~/.agents/flatmachines/auth.json`
|
|
45
45
|
- Token handling: pre-request refresh on expiry; if refresh fails, re-read auth store once for cross-process refresh; fallback refresh+retry on `401/403`.
|
|
46
46
|
- Retries on `429/500/502/503/504` with exponential backoff (no jitter).
|
|
@@ -52,6 +52,21 @@ Resolution: default → profile → overrides → override
|
|
|
52
52
|
- Inline flatagent config (`spec: flatagent`)
|
|
53
53
|
- Typed adapter ref: `{ type: "flatagent" | "smolagents" | "pi-agent", ref?: "...", config?: {...} }`
|
|
54
54
|
|
|
55
|
+
## LLM I/O & Formatting (Hard Rules)
|
|
56
|
+
|
|
57
|
+
- Between LLM stages, default to **plain text/Markdown** handoffs.
|
|
58
|
+
- Avoid JSON/Jinja-shaped model-to-model output unless required by strict schema validation or a boundary contract (API/DB/file format).
|
|
59
|
+
- Why: JSON/Jinja handoffs increase parse fragility and token overhead.
|
|
60
|
+
- Keep `input` / `output_to_context` mappings explicit and shallow.
|
|
61
|
+
- Push heavy transforms to boundary actions only (final save/write steps).
|
|
62
|
+
- Preserve full source text across stages (avoid excerpt-only chains unless explicitly requested).
|
|
63
|
+
- Jinja in config (`input`, `output_to_context`, `transitions`, `foreach`, `wait_for`) must be cross-SDK portable:
|
|
64
|
+
- use property access, comparisons, simple conditionals
|
|
65
|
+
- avoid Python-specific features (`.items()`, `|tojson`, `len()`, `isinstance()`, list comprehensions).
|
|
66
|
+
- **Never truncate** LLM inputs/outputs silently.
|
|
67
|
+
- If size is a problem: ask user, enforce prompt limits, reject+retry, or add a repair/compaction stage.
|
|
68
|
+
- Each transform must have a one-line justification; otherwise remove it.
|
|
69
|
+
|
|
55
70
|
## State Fields
|
|
56
71
|
|
|
57
72
|
| Field | Purpose |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flatagents
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.0
|
|
4
4
|
Summary: A lightweight framework for building LLM-powered agents.
|
|
5
5
|
Project-URL: Homepage, https://github.com/memgrafter/flatagents
|
|
6
6
|
Project-URL: Repository, https://github.com/memgrafter/flatagents
|
|
@@ -45,7 +45,7 @@ Description-Content-Type: text/markdown
|
|
|
45
45
|
|
|
46
46
|
Define single-call LLM agents in YAML. Use this package when you want **one structured call** per agent, with optional MCP tools and profile-driven model configs. For orchestration, install `flatmachines` separately.
|
|
47
47
|
|
|
48
|
-
**For LLM/machine readers:** see [
|
|
48
|
+
**For LLM/machine readers:** see [AGENTS.md](./AGENTS.md).
|
|
49
49
|
|
|
50
50
|
## Install
|
|
51
51
|
|
|
@@ -100,7 +100,7 @@ If `data.output` is provided, FlatAgents requests JSON mode and parses the respo
|
|
|
100
100
|
## Model Profiles (profiles.yml)
|
|
101
101
|
|
|
102
102
|
```yaml
|
|
103
|
-
spec:
|
|
103
|
+
spec: flatprofile
|
|
104
104
|
spec_version: "0.10.0"
|
|
105
105
|
|
|
106
106
|
data:
|
|
@@ -217,4 +217,5 @@ from flatagents import FlatMachine
|
|
|
217
217
|
|
|
218
218
|
Source of truth:
|
|
219
219
|
- [`flatagent.d.ts`](https://github.com/memgrafter/flatagents/blob/main/sdk/python/flatagents/flatagents/assets/flatagent.d.ts)
|
|
220
|
-
- [`
|
|
220
|
+
- [`prompt.d.ts`](https://github.com/memgrafter/flatagents/blob/main/sdk/python/flatagents/flatagents/assets/prompt.d.ts)
|
|
221
|
+
- [`profile.d.ts`](https://github.com/memgrafter/flatagents/blob/main/sdk/python/flatagents/flatagents/assets/profile.d.ts)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Define single-call LLM agents in YAML. Use this package when you want **one structured call** per agent, with optional MCP tools and profile-driven model configs. For orchestration, install `flatmachines` separately.
|
|
4
4
|
|
|
5
|
-
**For LLM/machine readers:** see [
|
|
5
|
+
**For LLM/machine readers:** see [AGENTS.md](./AGENTS.md).
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -57,7 +57,7 @@ If `data.output` is provided, FlatAgents requests JSON mode and parses the respo
|
|
|
57
57
|
## Model Profiles (profiles.yml)
|
|
58
58
|
|
|
59
59
|
```yaml
|
|
60
|
-
spec:
|
|
60
|
+
spec: flatprofile
|
|
61
61
|
spec_version: "0.10.0"
|
|
62
62
|
|
|
63
63
|
data:
|
|
@@ -174,4 +174,5 @@ from flatagents import FlatMachine
|
|
|
174
174
|
|
|
175
175
|
Source of truth:
|
|
176
176
|
- [`flatagent.d.ts`](https://github.com/memgrafter/flatagents/blob/main/sdk/python/flatagents/flatagents/assets/flatagent.d.ts)
|
|
177
|
-
- [`
|
|
177
|
+
- [`prompt.d.ts`](https://github.com/memgrafter/flatagents/blob/main/sdk/python/flatagents/flatagents/assets/prompt.d.ts)
|
|
178
|
+
- [`profile.d.ts`](https://github.com/memgrafter/flatagents/blob/main/sdk/python/flatagents/flatagents/assets/profile.d.ts)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
__version__ = "
|
|
1
|
+
__version__ = "4.0.0"
|
|
2
2
|
|
|
3
3
|
from .baseagent import (
|
|
4
4
|
# Base agent (abstract, for multi-step agents)
|
|
@@ -35,6 +35,8 @@ from .flatagent import FlatAgent
|
|
|
35
35
|
from .profiles import (
|
|
36
36
|
ProfileManager,
|
|
37
37
|
resolve_model_config,
|
|
38
|
+
load_profile_from_file,
|
|
39
|
+
resolve_profile_config,
|
|
38
40
|
)
|
|
39
41
|
from .validation import (
|
|
40
42
|
validate_flatagent_config,
|
|
@@ -63,6 +65,21 @@ from .tool_loop import (
|
|
|
63
65
|
StopReason,
|
|
64
66
|
SteeringProvider,
|
|
65
67
|
)
|
|
68
|
+
from .adapters import (
|
|
69
|
+
ClaudeCodeExecutor,
|
|
70
|
+
create_claude_code_executor,
|
|
71
|
+
CodexCliExecutor,
|
|
72
|
+
CodexAppServerTransport,
|
|
73
|
+
create_codex_cli_executor,
|
|
74
|
+
SmolagentsExecutor,
|
|
75
|
+
create_smolagents_executor,
|
|
76
|
+
PiAgentBridgeExecutor,
|
|
77
|
+
create_pi_agent_bridge_executor,
|
|
78
|
+
ClaudeCodeSessionHoldback,
|
|
79
|
+
ClaudeCodeForkResult,
|
|
80
|
+
CodexSessionHoldback,
|
|
81
|
+
CodexForkResult,
|
|
82
|
+
)
|
|
66
83
|
# Provider-specific utilities
|
|
67
84
|
from .providers import (
|
|
68
85
|
CerebrasRateLimits,
|
|
@@ -159,6 +176,22 @@ __all__ = [
|
|
|
159
176
|
# Model Profiles
|
|
160
177
|
"ProfileManager",
|
|
161
178
|
"resolve_model_config",
|
|
179
|
+
"load_profile_from_file",
|
|
180
|
+
"resolve_profile_config",
|
|
181
|
+
# Runtime adapters
|
|
182
|
+
"ClaudeCodeExecutor",
|
|
183
|
+
"create_claude_code_executor",
|
|
184
|
+
"CodexCliExecutor",
|
|
185
|
+
"CodexAppServerTransport",
|
|
186
|
+
"create_codex_cli_executor",
|
|
187
|
+
"SmolagentsExecutor",
|
|
188
|
+
"create_smolagents_executor",
|
|
189
|
+
"PiAgentBridgeExecutor",
|
|
190
|
+
"create_pi_agent_bridge_executor",
|
|
191
|
+
"ClaudeCodeSessionHoldback",
|
|
192
|
+
"ClaudeCodeForkResult",
|
|
193
|
+
"CodexSessionHoldback",
|
|
194
|
+
"CodexForkResult",
|
|
162
195
|
# Tool Use
|
|
163
196
|
"ToolResult",
|
|
164
197
|
"ToolProvider",
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Single-agent runtime adapters for FlatAgents."""
|
|
2
|
+
|
|
3
|
+
from .claude_code import ClaudeCodeExecutor, create_claude_code_executor
|
|
4
|
+
from .codex_cli import CodexCliExecutor, CodexAppServerTransport, create_codex_cli_executor
|
|
5
|
+
from .pi_agent_bridge import PiAgentBridgeExecutor, create_pi_agent_bridge_executor
|
|
6
|
+
from .claude_code_sessions import SessionHoldback as ClaudeCodeSessionHoldback, ForkResult as ClaudeCodeForkResult
|
|
7
|
+
from .codex_cli_sessions import CodexSessionHoldback, ForkResult as CodexForkResult
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"ClaudeCodeExecutor",
|
|
11
|
+
"create_claude_code_executor",
|
|
12
|
+
"CodexCliExecutor",
|
|
13
|
+
"CodexAppServerTransport",
|
|
14
|
+
"create_codex_cli_executor",
|
|
15
|
+
"PiAgentBridgeExecutor",
|
|
16
|
+
"create_pi_agent_bridge_executor",
|
|
17
|
+
"ClaudeCodeSessionHoldback",
|
|
18
|
+
"ClaudeCodeForkResult",
|
|
19
|
+
"CodexSessionHoldback",
|
|
20
|
+
"CodexForkResult",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
from .smolagents import SmolagentsExecutor, create_smolagents_executor
|
|
25
|
+
|
|
26
|
+
__all__.extend([
|
|
27
|
+
"SmolagentsExecutor",
|
|
28
|
+
"create_smolagents_executor",
|
|
29
|
+
])
|
|
30
|
+
except ImportError: # pragma: no cover - optional dependency
|
|
31
|
+
SmolagentsExecutor = None # type: ignore[assignment]
|
|
32
|
+
create_smolagents_executor = None # type: ignore[assignment]
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Async call throttle — serialized gate with jitter.
|
|
2
|
+
|
|
3
|
+
Enforces a minimum delay between calls. An asyncio.Lock serialises the
|
|
4
|
+
"when to launch" decision so concurrent callers are staggered, but the
|
|
5
|
+
actual work (subprocess, network call, …) runs concurrently once past
|
|
6
|
+
the gate.
|
|
7
|
+
|
|
8
|
+
throttle = CallThrottle(delay=3.0, jitter=4.0)
|
|
9
|
+
|
|
10
|
+
# 3 concurrent tasks:
|
|
11
|
+
# t≈0s task-1 passes gate immediately (first call)
|
|
12
|
+
# t≈0s task-2 acquires lock, sleeps ~7s, passes gate
|
|
13
|
+
# t≈7s task-3 acquires lock, sleeps ~5s, passes gate
|
|
14
|
+
# all 3 subprocesses now running concurrently
|
|
15
|
+
|
|
16
|
+
Delay formula per call::
|
|
17
|
+
|
|
18
|
+
wait = max(0, (last_call + delay + uniform(0, 2*jitter)) - now)
|
|
19
|
+
|
|
20
|
+
With ``delay=3, jitter=4`` the gap between consecutive calls is
|
|
21
|
+
uniformly distributed over **[3, 11] seconds**, with millisecond
|
|
22
|
+
granularity from float arithmetic.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import asyncio
|
|
28
|
+
import logging
|
|
29
|
+
import random
|
|
30
|
+
import time
|
|
31
|
+
|
|
32
|
+
logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class CallThrottle:
|
|
36
|
+
"""Async rate limiter: base delay + uniform jitter between calls.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
delay: Minimum base seconds between calls.
|
|
40
|
+
jitter: Half-width of the jitter window. Actual jitter added is
|
|
41
|
+
``uniform(0, 2 * jitter)`` so the total gap is in
|
|
42
|
+
``[delay, delay + 2*jitter]``. Set to 0 for fixed delay.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, delay: float = 0.0, jitter: float = 0.0) -> None:
|
|
46
|
+
self._delay = max(0.0, delay)
|
|
47
|
+
self._jitter = max(0.0, jitter)
|
|
48
|
+
self._lock = asyncio.Lock()
|
|
49
|
+
self._last_call: float = 0.0 # monotonic timestamp
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def enabled(self) -> bool:
|
|
53
|
+
return self._delay > 0 or self._jitter > 0
|
|
54
|
+
|
|
55
|
+
async def wait(self) -> float:
|
|
56
|
+
"""Block until the next call is allowed.
|
|
57
|
+
|
|
58
|
+
Returns the number of seconds actually waited (0.0 on first call
|
|
59
|
+
or when the throttle is disabled).
|
|
60
|
+
"""
|
|
61
|
+
if not self.enabled:
|
|
62
|
+
return 0.0
|
|
63
|
+
|
|
64
|
+
async with self._lock:
|
|
65
|
+
now = time.monotonic()
|
|
66
|
+
|
|
67
|
+
if self._last_call == 0.0:
|
|
68
|
+
# First call — no wait
|
|
69
|
+
self._last_call = now
|
|
70
|
+
return 0.0
|
|
71
|
+
|
|
72
|
+
# Compute jitter with ms granularity
|
|
73
|
+
jitter_ms = random.randint(0, int(2 * self._jitter * 1000))
|
|
74
|
+
jitter_s = jitter_ms / 1000.0
|
|
75
|
+
|
|
76
|
+
target = self._last_call + self._delay + jitter_s
|
|
77
|
+
wait_s = max(0.0, target - now)
|
|
78
|
+
|
|
79
|
+
if wait_s > 0:
|
|
80
|
+
logger.debug(
|
|
81
|
+
"CallThrottle: sleeping %.3fs (delay=%.1f jitter=%.3f)",
|
|
82
|
+
wait_s, self._delay, jitter_s,
|
|
83
|
+
)
|
|
84
|
+
await asyncio.sleep(wait_s)
|
|
85
|
+
|
|
86
|
+
self._last_call = time.monotonic()
|
|
87
|
+
return wait_s
|
|
88
|
+
|
|
89
|
+
def reset(self) -> None:
|
|
90
|
+
"""Reset the throttle so the next call passes immediately."""
|
|
91
|
+
self._last_call = 0.0
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# ---------------------------------------------------------------------------
|
|
95
|
+
# Factory from config dict
|
|
96
|
+
# ---------------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
def throttle_from_config(config: dict) -> CallThrottle:
|
|
99
|
+
"""Create a CallThrottle from an adapter config dict.
|
|
100
|
+
|
|
101
|
+
Recognised keys::
|
|
102
|
+
|
|
103
|
+
rate_limit_delay: 3.0 # base seconds (default 0 = disabled)
|
|
104
|
+
rate_limit_jitter: 4.0 # ±seconds (default 0)
|
|
105
|
+
|
|
106
|
+
Returns a disabled throttle when neither key is set.
|
|
107
|
+
"""
|
|
108
|
+
delay = float(config.get("rate_limit_delay", 0))
|
|
109
|
+
jitter = float(config.get("rate_limit_jitter", 0))
|
|
110
|
+
return CallThrottle(delay=delay, jitter=jitter)
|