linch 1.0.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.
- linch/__init__.py +487 -0
- linch/_blocking.py +71 -0
- linch/_http_errors.py +99 -0
- linch/_prompt_cache.py +120 -0
- linch/_version.py +26 -0
- linch/abort.py +77 -0
- linch/agent.py +1088 -0
- linch/budget.py +93 -0
- linch/compaction.py +521 -0
- linch/config.py +97 -0
- linch/context/__init__.py +21 -0
- linch/context/builder.py +216 -0
- linch/coordination/__init__.py +57 -0
- linch/coordination/mailbox/__init__.py +13 -0
- linch/coordination/mailbox/core.py +80 -0
- linch/coordination/mailbox/correlation.py +51 -0
- linch/coordination/scheduling/__init__.py +19 -0
- linch/coordination/scheduling/cron.py +105 -0
- linch/coordination/scheduling/loop.py +114 -0
- linch/coordination/scheduling/schedule.py +71 -0
- linch/coordination/scheduling/sqlite.py +142 -0
- linch/coordination/scheduling/store.py +61 -0
- linch/coordination/scheduling/tools.py +72 -0
- linch/coordination/send_message.py +107 -0
- linch/deep_agent/__init__.py +9 -0
- linch/deep_agent/factory.py +194 -0
- linch/deep_agent/prompts.py +126 -0
- linch/deep_agent/subagents.py +127 -0
- linch/errors.py +64 -0
- linch/evals/__init__.py +46 -0
- linch/evals/harness.py +226 -0
- linch/evals/scorers.py +215 -0
- linch/evals/scripted.py +96 -0
- linch/events.py +887 -0
- linch/filesystem/__init__.py +54 -0
- linch/filesystem/backend.py +253 -0
- linch/filesystem/disk.py +119 -0
- linch/filesystem/offload.py +140 -0
- linch/filesystem/postgres.py +180 -0
- linch/filesystem/sqlite.py +157 -0
- linch/filesystem/tools.py +287 -0
- linch/hooks/__init__.py +71 -0
- linch/hooks/adapters.py +378 -0
- linch/hooks/contexts.py +166 -0
- linch/hooks/dispatcher.py +212 -0
- linch/hooks/memory.py +117 -0
- linch/hooks/types.py +101 -0
- linch/loop/__init__.py +30 -0
- linch/loop/checkpoint.py +196 -0
- linch/loop/dispatch.py +225 -0
- linch/loop/finalize.py +433 -0
- linch/loop/request.py +243 -0
- linch/loop/runner.py +1412 -0
- linch/loop/streaming.py +292 -0
- linch/loop/terminals.py +369 -0
- linch/loop_guard/__init__.py +17 -0
- linch/loop_guard/guard.py +164 -0
- linch/mcp/__init__.py +51 -0
- linch/mcp/client.py +163 -0
- linch/mcp/config.py +29 -0
- linch/mcp/naming.py +13 -0
- linch/mcp/permission_bridge.py +32 -0
- linch/mcp/result.py +64 -0
- linch/mcp/tool.py +91 -0
- linch/memory/__init__.py +28 -0
- linch/memory/builder.py +135 -0
- linch/memory/keyword.py +94 -0
- linch/memory/lifecycle.py +93 -0
- linch/memory/postgres.py +188 -0
- linch/memory/sqlite.py +169 -0
- linch/memory/store.py +40 -0
- linch/memory/tiered.py +183 -0
- linch/memory/tools.py +188 -0
- linch/memory/types.py +25 -0
- linch/middleware.py +160 -0
- linch/observability/__init__.py +32 -0
- linch/observability/dispatcher.py +67 -0
- linch/observability/otel.py +222 -0
- linch/observability/protocol.py +155 -0
- linch/observability/reference.py +243 -0
- linch/openai_responses.py +357 -0
- linch/permissions/__init__.py +25 -0
- linch/permissions/engine.py +295 -0
- linch/permissions/keys.py +39 -0
- linch/permissions/rules.py +321 -0
- linch/permissions/ruleset.py +45 -0
- linch/pricing.py +95 -0
- linch/providers/__init__.py +47 -0
- linch/providers/anthropic.py +467 -0
- linch/providers/base.py +87 -0
- linch/providers/catalog.py +137 -0
- linch/providers/gemini.py +296 -0
- linch/providers/llamacpp.py +178 -0
- linch/providers/openai_chat.py +317 -0
- linch/providers/openai_responses.py +54 -0
- linch/providers/retry.py +57 -0
- linch/providers/sglang.py +76 -0
- linch/providers/vllm.py +59 -0
- linch/reports.py +400 -0
- linch/run_store.py +520 -0
- linch/scheduler.py +1112 -0
- linch/session.py +260 -0
- linch/sessions/__init__.py +16 -0
- linch/sessions/memory.py +200 -0
- linch/sessions/postgres.py +634 -0
- linch/sessions/sqlite.py +554 -0
- linch/sessions/store.py +74 -0
- linch/sessions/tasks.py +44 -0
- linch/skills/__init__.py +23 -0
- linch/skills/builtins.py +62 -0
- linch/skills/listing.py +65 -0
- linch/skills/loader.py +222 -0
- linch/skills/overlay.py +7 -0
- linch/skills/shell_split.py +49 -0
- linch/skills/substitute.py +25 -0
- linch/skills/system_reminder.py +5 -0
- linch/skills/types.py +31 -0
- linch/storage/__init__.py +3 -0
- linch/storage/_executor.py +133 -0
- linch/storage/_pg.py +38 -0
- linch/subagents/__init__.py +34 -0
- linch/subagents/builtins.py +57 -0
- linch/subagents/default_agent.py +24 -0
- linch/subagents/generator.py +377 -0
- linch/subagents/loader.py +181 -0
- linch/subagents/registry.py +46 -0
- linch/subagents/runner.py +399 -0
- linch/subagents/types.py +38 -0
- linch/subagents/workers.py +26 -0
- linch/tools/__init__.py +47 -0
- linch/tools/_worker_utils.py +18 -0
- linch/tools/ask_user.py +237 -0
- linch/tools/base.py +97 -0
- linch/tools/builtin.py +833 -0
- linch/tools/execution.py +294 -0
- linch/tools/file_tracker.py +27 -0
- linch/tools/function.py +239 -0
- linch/tools/isolation.py +67 -0
- linch/tools/registry.py +176 -0
- linch/tools/skill.py +126 -0
- linch/tools/subagent.py +320 -0
- linch/tools/subagent_continue.py +152 -0
- linch/tools/subagent_stop.py +103 -0
- linch/tools/tasks.py +222 -0
- linch/types.py +275 -0
- linch/verification.py +133 -0
- linch/workflow/__init__.py +29 -0
- linch/workflow/context.py +199 -0
- linch/workflow/engine.py +87 -0
- linch/workflow/journal.py +66 -0
- linch-1.0.0.dist-info/METADATA +426 -0
- linch-1.0.0.dist-info/RECORD +154 -0
- linch-1.0.0.dist-info/WHEEL +4 -0
- linch-1.0.0.dist-info/licenses/LICENSE +21 -0
linch/__init__.py
ADDED
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
"""Public API for linch."""
|
|
2
|
+
|
|
3
|
+
from ._version import get_version
|
|
4
|
+
from .agent import Agent, AgentOptions
|
|
5
|
+
from .budget import RunBudget
|
|
6
|
+
from .compaction import CompactionLadder, DefaultCompaction, DetailedCompaction
|
|
7
|
+
from .config import FeatureFlags, SystemPromptConfig, SystemPromptSection
|
|
8
|
+
from .context import (
|
|
9
|
+
ContextBudget,
|
|
10
|
+
ContextBuilder,
|
|
11
|
+
ContextBuilderChain,
|
|
12
|
+
ContextBuildResult,
|
|
13
|
+
ContextBuildTurn,
|
|
14
|
+
)
|
|
15
|
+
from .coordination.mailbox import Correlator, InMemoryMailbox, Mailbox, MailboxMessage
|
|
16
|
+
from .coordination.scheduling import (
|
|
17
|
+
InMemoryScheduleStore,
|
|
18
|
+
Schedule,
|
|
19
|
+
SchedulerLoop,
|
|
20
|
+
ScheduleStore,
|
|
21
|
+
SqliteScheduleStore,
|
|
22
|
+
cron_matches,
|
|
23
|
+
next_cron_time,
|
|
24
|
+
schedule_tools,
|
|
25
|
+
validate_cron,
|
|
26
|
+
)
|
|
27
|
+
from .deep_agent import DEEP_AGENT_SYSTEM_PROMPT, create_deep_agent
|
|
28
|
+
from .errors import (
|
|
29
|
+
AbortError,
|
|
30
|
+
AuthError,
|
|
31
|
+
ConfigError,
|
|
32
|
+
ContextLengthError,
|
|
33
|
+
LinchError,
|
|
34
|
+
PermissionDeniedError,
|
|
35
|
+
ProviderError,
|
|
36
|
+
RateLimitError,
|
|
37
|
+
SkillError,
|
|
38
|
+
ToolExecutionError,
|
|
39
|
+
ToolTimeoutError,
|
|
40
|
+
)
|
|
41
|
+
from .events import (
|
|
42
|
+
AssistantEvent,
|
|
43
|
+
BudgetEvent,
|
|
44
|
+
CompactionEvent,
|
|
45
|
+
ContextBuildEvent,
|
|
46
|
+
ErrorEvent,
|
|
47
|
+
Event,
|
|
48
|
+
HookEventRecord,
|
|
49
|
+
LoopGuardEvent,
|
|
50
|
+
ModelFallbackEvent,
|
|
51
|
+
PartialAssistantEvent,
|
|
52
|
+
PermissionRequestEvent,
|
|
53
|
+
ResultEvent,
|
|
54
|
+
ScheduleEvent,
|
|
55
|
+
SkillCompletedEvent,
|
|
56
|
+
SkillInvokedEvent,
|
|
57
|
+
SkillsLoadedEvent,
|
|
58
|
+
SubagentEvent,
|
|
59
|
+
SystemEvent,
|
|
60
|
+
ToolCallEndEvent,
|
|
61
|
+
ToolCallStartEvent,
|
|
62
|
+
UsageEvent,
|
|
63
|
+
UserEvent,
|
|
64
|
+
VerificationEvent,
|
|
65
|
+
WorkflowEvent,
|
|
66
|
+
is_budget_event,
|
|
67
|
+
is_context_build_event,
|
|
68
|
+
is_hook_event,
|
|
69
|
+
is_loop_guard_event,
|
|
70
|
+
is_subagent_event,
|
|
71
|
+
is_verification_event,
|
|
72
|
+
is_workflow_event,
|
|
73
|
+
)
|
|
74
|
+
from .filesystem import (
|
|
75
|
+
CompositeFileBackend,
|
|
76
|
+
DiskFileBackend,
|
|
77
|
+
FileBackend,
|
|
78
|
+
OffloadConfig,
|
|
79
|
+
SqliteFileBackend,
|
|
80
|
+
StateFileBackend,
|
|
81
|
+
filesystem_tools,
|
|
82
|
+
)
|
|
83
|
+
from .hooks import (
|
|
84
|
+
AfterProviderCallContext,
|
|
85
|
+
BeforeFinalAnswerContext,
|
|
86
|
+
BeforeProviderCallContext,
|
|
87
|
+
ContextInjectionHook,
|
|
88
|
+
FinalAnswerVerifierHook,
|
|
89
|
+
HookContext,
|
|
90
|
+
HookDispatcher,
|
|
91
|
+
HookDispatchResult,
|
|
92
|
+
HookEvent,
|
|
93
|
+
HookResult,
|
|
94
|
+
MemoryExtractionHook,
|
|
95
|
+
PostCompactContext,
|
|
96
|
+
PostToolUseContext,
|
|
97
|
+
PostToolUseFailureContext,
|
|
98
|
+
PreCompactContext,
|
|
99
|
+
PreToolUseContext,
|
|
100
|
+
RunTelemetryHook,
|
|
101
|
+
StopContext,
|
|
102
|
+
StopPredicateHook,
|
|
103
|
+
SubagentStartContext,
|
|
104
|
+
SubagentStopContext,
|
|
105
|
+
ToolMiddlewareHook,
|
|
106
|
+
UserPromptSubmitContext,
|
|
107
|
+
normalize_hooks,
|
|
108
|
+
)
|
|
109
|
+
from .loop import apply_provider_capabilities
|
|
110
|
+
from .loop_guard import (
|
|
111
|
+
LoopGuard,
|
|
112
|
+
LoopGuardDecision,
|
|
113
|
+
LoopGuardState,
|
|
114
|
+
evaluate_loop_guard,
|
|
115
|
+
normalize_loop_guard,
|
|
116
|
+
)
|
|
117
|
+
from .mcp import (
|
|
118
|
+
McpHttpServerConfig,
|
|
119
|
+
McpServerConfig,
|
|
120
|
+
McpStdioServerConfig,
|
|
121
|
+
connect_mcp_servers,
|
|
122
|
+
)
|
|
123
|
+
from .memory import (
|
|
124
|
+
ConsolidationGate,
|
|
125
|
+
InMemoryKeywordMemoryStore,
|
|
126
|
+
MemoryContextBuilder,
|
|
127
|
+
MemoryExtractionContext,
|
|
128
|
+
MemoryExtractor,
|
|
129
|
+
MemoryItem,
|
|
130
|
+
MemorySearchResult,
|
|
131
|
+
MemorySearchTool,
|
|
132
|
+
MemoryStore,
|
|
133
|
+
MemoryUpsertTool,
|
|
134
|
+
PostgresMemoryStore,
|
|
135
|
+
SqliteMemoryStore,
|
|
136
|
+
TieredMemoryStore,
|
|
137
|
+
)
|
|
138
|
+
from .middleware import (
|
|
139
|
+
AgentMiddleware,
|
|
140
|
+
MiddlewareContext,
|
|
141
|
+
ToolCallMiddlewareInput,
|
|
142
|
+
ToolCallMiddlewareResult,
|
|
143
|
+
)
|
|
144
|
+
from .observability import (
|
|
145
|
+
BaseObserver,
|
|
146
|
+
LoggingObserver,
|
|
147
|
+
ObserverDispatcher,
|
|
148
|
+
OpenTelemetryObserver,
|
|
149
|
+
ProviderCallInfo,
|
|
150
|
+
ProviderCallResult,
|
|
151
|
+
RunInfo,
|
|
152
|
+
RunObserver,
|
|
153
|
+
RunResultInfo,
|
|
154
|
+
Span,
|
|
155
|
+
SpanCollector,
|
|
156
|
+
ToolInfo,
|
|
157
|
+
ToolResultInfo,
|
|
158
|
+
TurnInfo,
|
|
159
|
+
normalize_observers,
|
|
160
|
+
)
|
|
161
|
+
from .openai_responses import OpenAIOptions, OpenAIReasoning
|
|
162
|
+
from .providers import (
|
|
163
|
+
AnthropicProvider,
|
|
164
|
+
AnthropicProviderOptions,
|
|
165
|
+
BaseProvider,
|
|
166
|
+
GeminiProvider,
|
|
167
|
+
GeminiProviderOptions,
|
|
168
|
+
LlamaCppProvider,
|
|
169
|
+
LlamaCppProviderOptions,
|
|
170
|
+
OpenAIChatCompletionsProvider,
|
|
171
|
+
OpenAIChatProviderOptions,
|
|
172
|
+
OpenAIResponsesProvider,
|
|
173
|
+
OpenAIResponsesProviderOptions,
|
|
174
|
+
ProviderCapabilities,
|
|
175
|
+
ProviderModelInfo,
|
|
176
|
+
SGLangProvider,
|
|
177
|
+
SGLangProviderOptions,
|
|
178
|
+
VLLMProvider,
|
|
179
|
+
VLLMProviderOptions,
|
|
180
|
+
get_provider_model_info,
|
|
181
|
+
list_provider_models,
|
|
182
|
+
)
|
|
183
|
+
from .providers.retry import RetryOptions
|
|
184
|
+
from .reports import RunReport, build_run_report, load_run_report
|
|
185
|
+
from .run_store import (
|
|
186
|
+
SCHEMA_VERSION as RUN_SCHEMA_VERSION,
|
|
187
|
+
)
|
|
188
|
+
from .run_store import (
|
|
189
|
+
InMemoryRunStore,
|
|
190
|
+
RunCheckpoint,
|
|
191
|
+
RunRecord,
|
|
192
|
+
RunStore,
|
|
193
|
+
SqliteRunStore,
|
|
194
|
+
StoredRunEvent,
|
|
195
|
+
)
|
|
196
|
+
from .session import RunOptions, Session
|
|
197
|
+
from .subagents import (
|
|
198
|
+
CreatedSubagentDefinition,
|
|
199
|
+
GeneratedSubagentDefinition,
|
|
200
|
+
create_subagent_definition,
|
|
201
|
+
generate_subagent_definition,
|
|
202
|
+
render_subagent_markdown,
|
|
203
|
+
write_subagent_definition,
|
|
204
|
+
)
|
|
205
|
+
from .tools import (
|
|
206
|
+
AskUserHandler,
|
|
207
|
+
AskUserOption,
|
|
208
|
+
AskUserQuestion,
|
|
209
|
+
AskUserRequest,
|
|
210
|
+
AskUserResponse,
|
|
211
|
+
AskUserTool,
|
|
212
|
+
Citation,
|
|
213
|
+
FileReadTracker,
|
|
214
|
+
FunctionTool,
|
|
215
|
+
ResourceAccess,
|
|
216
|
+
ResourceMode,
|
|
217
|
+
Tool,
|
|
218
|
+
ToolContext,
|
|
219
|
+
ToolRegistry,
|
|
220
|
+
ToolResult,
|
|
221
|
+
default_tools,
|
|
222
|
+
tool,
|
|
223
|
+
)
|
|
224
|
+
from .tools.isolation import IsolationBackend, TempDirIsolation
|
|
225
|
+
from .tools.registry import empty_tools, tools_from_defaults
|
|
226
|
+
from .types import (
|
|
227
|
+
ContentBlock,
|
|
228
|
+
ImageBlock,
|
|
229
|
+
Message,
|
|
230
|
+
ModelId,
|
|
231
|
+
OutputSchema,
|
|
232
|
+
PermissionMode,
|
|
233
|
+
RedactedThinkingBlock,
|
|
234
|
+
StopReason,
|
|
235
|
+
TextBlock,
|
|
236
|
+
ThinkingBlock,
|
|
237
|
+
ToolChoice,
|
|
238
|
+
ToolResultBlock,
|
|
239
|
+
ToolUseBlock,
|
|
240
|
+
Usage,
|
|
241
|
+
)
|
|
242
|
+
from .verification import (
|
|
243
|
+
ScorerVerifier,
|
|
244
|
+
Verdict,
|
|
245
|
+
VerificationContext,
|
|
246
|
+
Verifier,
|
|
247
|
+
evaluate_verifiers,
|
|
248
|
+
normalize_verifiers,
|
|
249
|
+
)
|
|
250
|
+
from .workflow import WorkflowContext, WorkflowError, WorkflowJournal
|
|
251
|
+
|
|
252
|
+
defaultTools = default_tools
|
|
253
|
+
__version__ = get_version()
|
|
254
|
+
|
|
255
|
+
__all__ = [
|
|
256
|
+
"AbortError",
|
|
257
|
+
"AskUserHandler",
|
|
258
|
+
"AskUserOption",
|
|
259
|
+
"AskUserQuestion",
|
|
260
|
+
"AskUserRequest",
|
|
261
|
+
"AskUserResponse",
|
|
262
|
+
"AskUserTool",
|
|
263
|
+
"Agent",
|
|
264
|
+
"LinchError",
|
|
265
|
+
"AgentOptions",
|
|
266
|
+
"RunBudget",
|
|
267
|
+
"BudgetEvent",
|
|
268
|
+
"is_budget_event",
|
|
269
|
+
"WorkflowContext",
|
|
270
|
+
"WorkflowError",
|
|
271
|
+
"WorkflowEvent",
|
|
272
|
+
"WorkflowJournal",
|
|
273
|
+
"is_workflow_event",
|
|
274
|
+
"ScheduleEvent",
|
|
275
|
+
"Schedule",
|
|
276
|
+
"ScheduleStore",
|
|
277
|
+
"InMemoryScheduleStore",
|
|
278
|
+
"SqliteScheduleStore",
|
|
279
|
+
"SchedulerLoop",
|
|
280
|
+
"schedule_tools",
|
|
281
|
+
"cron_matches",
|
|
282
|
+
"next_cron_time",
|
|
283
|
+
"validate_cron",
|
|
284
|
+
"AgentMiddleware",
|
|
285
|
+
"ContextBudget",
|
|
286
|
+
"ContextBuilder",
|
|
287
|
+
"ContextBuilderChain",
|
|
288
|
+
"ContextBuildEvent",
|
|
289
|
+
"ContextBuildResult",
|
|
290
|
+
"ContextBuildTurn",
|
|
291
|
+
"CompactionLadder",
|
|
292
|
+
"DefaultCompaction",
|
|
293
|
+
"DetailedCompaction",
|
|
294
|
+
"DEEP_AGENT_SYSTEM_PROMPT",
|
|
295
|
+
"FeatureFlags",
|
|
296
|
+
"CompositeFileBackend",
|
|
297
|
+
"DiskFileBackend",
|
|
298
|
+
"FileBackend",
|
|
299
|
+
"OffloadConfig",
|
|
300
|
+
"SqliteFileBackend",
|
|
301
|
+
"StateFileBackend",
|
|
302
|
+
"filesystem_tools",
|
|
303
|
+
"InMemoryKeywordMemoryStore",
|
|
304
|
+
"InMemoryRunStore",
|
|
305
|
+
"SystemPromptConfig",
|
|
306
|
+
"SystemPromptSection",
|
|
307
|
+
"OutputSchema",
|
|
308
|
+
"ToolChoice",
|
|
309
|
+
"empty_tools",
|
|
310
|
+
"tools_from_defaults",
|
|
311
|
+
"AssistantEvent",
|
|
312
|
+
"CompactionEvent",
|
|
313
|
+
"ContentBlock",
|
|
314
|
+
"ErrorEvent",
|
|
315
|
+
"Event",
|
|
316
|
+
"HookEvent",
|
|
317
|
+
"HookEventRecord",
|
|
318
|
+
"HookResult",
|
|
319
|
+
"HookContext",
|
|
320
|
+
"HookDispatchResult",
|
|
321
|
+
"HookDispatcher",
|
|
322
|
+
"UserPromptSubmitContext",
|
|
323
|
+
"BeforeProviderCallContext",
|
|
324
|
+
"AfterProviderCallContext",
|
|
325
|
+
"PreToolUseContext",
|
|
326
|
+
"PostToolUseContext",
|
|
327
|
+
"PostToolUseFailureContext",
|
|
328
|
+
"PreCompactContext",
|
|
329
|
+
"PostCompactContext",
|
|
330
|
+
"BeforeFinalAnswerContext",
|
|
331
|
+
"StopContext",
|
|
332
|
+
"SubagentStartContext",
|
|
333
|
+
"SubagentStopContext",
|
|
334
|
+
"ContextInjectionHook",
|
|
335
|
+
"ToolMiddlewareHook",
|
|
336
|
+
"FinalAnswerVerifierHook",
|
|
337
|
+
"StopPredicateHook",
|
|
338
|
+
"RunTelemetryHook",
|
|
339
|
+
"MemoryExtractionHook",
|
|
340
|
+
"normalize_hooks",
|
|
341
|
+
"is_hook_event",
|
|
342
|
+
"ImageBlock",
|
|
343
|
+
"AuthError",
|
|
344
|
+
"ConfigError",
|
|
345
|
+
"ContextLengthError",
|
|
346
|
+
"CreatedSubagentDefinition",
|
|
347
|
+
"Citation",
|
|
348
|
+
"FileReadTracker",
|
|
349
|
+
"FunctionTool",
|
|
350
|
+
"GeneratedSubagentDefinition",
|
|
351
|
+
"McpHttpServerConfig",
|
|
352
|
+
"McpServerConfig",
|
|
353
|
+
"McpStdioServerConfig",
|
|
354
|
+
"ConsolidationGate",
|
|
355
|
+
"MemoryContextBuilder",
|
|
356
|
+
"MemoryExtractionContext",
|
|
357
|
+
"MemoryExtractor",
|
|
358
|
+
"MemoryItem",
|
|
359
|
+
"MemorySearchResult",
|
|
360
|
+
"MemorySearchTool",
|
|
361
|
+
"MemoryStore",
|
|
362
|
+
"MemoryUpsertTool",
|
|
363
|
+
"MiddlewareContext",
|
|
364
|
+
"Message",
|
|
365
|
+
"ModelId",
|
|
366
|
+
"OpenAIOptions",
|
|
367
|
+
"OpenAIReasoning",
|
|
368
|
+
"PartialAssistantEvent",
|
|
369
|
+
"PermissionMode",
|
|
370
|
+
"PermissionDeniedError",
|
|
371
|
+
"PermissionRequestEvent",
|
|
372
|
+
"ProviderError",
|
|
373
|
+
"PostgresMemoryStore",
|
|
374
|
+
"RateLimitError",
|
|
375
|
+
"RedactedThinkingBlock",
|
|
376
|
+
"ResultEvent",
|
|
377
|
+
"RunOptions",
|
|
378
|
+
"ResourceAccess",
|
|
379
|
+
"ResourceMode",
|
|
380
|
+
"SkillCompletedEvent",
|
|
381
|
+
"SkillError",
|
|
382
|
+
"ThinkingBlock",
|
|
383
|
+
"SkillInvokedEvent",
|
|
384
|
+
"SkillsLoadedEvent",
|
|
385
|
+
"StopReason",
|
|
386
|
+
"SubagentEvent",
|
|
387
|
+
"SystemEvent",
|
|
388
|
+
"Session",
|
|
389
|
+
"SqliteMemoryStore",
|
|
390
|
+
"TieredMemoryStore",
|
|
391
|
+
"TextBlock",
|
|
392
|
+
"Tool",
|
|
393
|
+
"ToolCallEndEvent",
|
|
394
|
+
"ToolCallMiddlewareInput",
|
|
395
|
+
"ToolCallMiddlewareResult",
|
|
396
|
+
"ToolCallStartEvent",
|
|
397
|
+
"ToolContext",
|
|
398
|
+
"ToolExecutionError",
|
|
399
|
+
"ToolTimeoutError",
|
|
400
|
+
"ToolRegistry",
|
|
401
|
+
"ToolResultBlock",
|
|
402
|
+
"ToolResult",
|
|
403
|
+
"ToolUseBlock",
|
|
404
|
+
"tool",
|
|
405
|
+
"Usage",
|
|
406
|
+
"UsageEvent",
|
|
407
|
+
"UserEvent",
|
|
408
|
+
"AnthropicProvider",
|
|
409
|
+
"AnthropicProviderOptions",
|
|
410
|
+
"BaseProvider",
|
|
411
|
+
"GeminiProvider",
|
|
412
|
+
"GeminiProviderOptions",
|
|
413
|
+
"LlamaCppProvider",
|
|
414
|
+
"LlamaCppProviderOptions",
|
|
415
|
+
"OpenAIChatCompletionsProvider",
|
|
416
|
+
"OpenAIChatProviderOptions",
|
|
417
|
+
"OpenAIResponsesProvider",
|
|
418
|
+
"OpenAIResponsesProviderOptions",
|
|
419
|
+
"ProviderCapabilities",
|
|
420
|
+
"ProviderModelInfo",
|
|
421
|
+
"RetryOptions",
|
|
422
|
+
"SGLangProvider",
|
|
423
|
+
"SGLangProviderOptions",
|
|
424
|
+
"RunReport",
|
|
425
|
+
"RUN_SCHEMA_VERSION",
|
|
426
|
+
"RunCheckpoint",
|
|
427
|
+
"RunRecord",
|
|
428
|
+
"RunStore",
|
|
429
|
+
"SqliteRunStore",
|
|
430
|
+
"StoredRunEvent",
|
|
431
|
+
"VLLMProvider",
|
|
432
|
+
"VLLMProviderOptions",
|
|
433
|
+
"apply_provider_capabilities",
|
|
434
|
+
"build_run_report",
|
|
435
|
+
"create_subagent_definition",
|
|
436
|
+
"LoopGuard",
|
|
437
|
+
"LoopGuardDecision",
|
|
438
|
+
"LoopGuardEvent",
|
|
439
|
+
"Correlator",
|
|
440
|
+
"InMemoryMailbox",
|
|
441
|
+
"Mailbox",
|
|
442
|
+
"MailboxMessage",
|
|
443
|
+
"IsolationBackend",
|
|
444
|
+
"TempDirIsolation",
|
|
445
|
+
"ModelFallbackEvent",
|
|
446
|
+
"LoopGuardState",
|
|
447
|
+
"evaluate_loop_guard",
|
|
448
|
+
"normalize_loop_guard",
|
|
449
|
+
"ScorerVerifier",
|
|
450
|
+
"Verdict",
|
|
451
|
+
"VerificationContext",
|
|
452
|
+
"VerificationEvent",
|
|
453
|
+
"Verifier",
|
|
454
|
+
"evaluate_verifiers",
|
|
455
|
+
"is_verification_event",
|
|
456
|
+
"normalize_verifiers",
|
|
457
|
+
"connect_mcp_servers",
|
|
458
|
+
"create_deep_agent",
|
|
459
|
+
"defaultTools",
|
|
460
|
+
"default_tools",
|
|
461
|
+
"get_version",
|
|
462
|
+
"__version__",
|
|
463
|
+
"is_context_build_event",
|
|
464
|
+
"get_provider_model_info",
|
|
465
|
+
"generate_subagent_definition",
|
|
466
|
+
"is_loop_guard_event",
|
|
467
|
+
"is_subagent_event",
|
|
468
|
+
"list_provider_models",
|
|
469
|
+
"load_run_report",
|
|
470
|
+
"BaseObserver",
|
|
471
|
+
"LoggingObserver",
|
|
472
|
+
"ObserverDispatcher",
|
|
473
|
+
"OpenTelemetryObserver",
|
|
474
|
+
"ProviderCallInfo",
|
|
475
|
+
"ProviderCallResult",
|
|
476
|
+
"RunInfo",
|
|
477
|
+
"RunObserver",
|
|
478
|
+
"RunResultInfo",
|
|
479
|
+
"Span",
|
|
480
|
+
"SpanCollector",
|
|
481
|
+
"ToolInfo",
|
|
482
|
+
"ToolResultInfo",
|
|
483
|
+
"TurnInfo",
|
|
484
|
+
"normalize_observers",
|
|
485
|
+
"render_subagent_markdown",
|
|
486
|
+
"write_subagent_definition",
|
|
487
|
+
]
|
linch/_blocking.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Bounded, daemon-thread offload for blocking work.
|
|
2
|
+
|
|
3
|
+
The core loop must never run blocking disk/DB/CPU work directly on the event
|
|
4
|
+
loop thread. ``asyncio.to_thread`` dispatches onto the default executor whose
|
|
5
|
+
*non-daemon* worker threads can keep the interpreter (and the managed test
|
|
6
|
+
sandbox) alive at teardown, and an unbounded ``threading.Thread`` per call has
|
|
7
|
+
no backpressure.
|
|
8
|
+
|
|
9
|
+
``run_blocking`` threads the needle: each call runs on a fresh *daemon* thread
|
|
10
|
+
(never blocks teardown) and a per-loop semaphore caps how many run at once
|
|
11
|
+
(backpressure). Wakeup is via ``loop.call_soon_threadsafe`` so the awaiting
|
|
12
|
+
coroutine resumes reliably.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import asyncio
|
|
18
|
+
import threading
|
|
19
|
+
from collections.abc import Callable
|
|
20
|
+
from typing import Any, TypeVar
|
|
21
|
+
|
|
22
|
+
T = TypeVar("T")
|
|
23
|
+
|
|
24
|
+
# Cap on concurrently-offloaded blocking calls per event loop. Mirrors the
|
|
25
|
+
# default thread-pool sizing intent without sharing a global pool across loops.
|
|
26
|
+
_MAX_CONCURRENCY = 32
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _loop_semaphore(loop: asyncio.AbstractEventLoop) -> asyncio.Semaphore:
|
|
30
|
+
sem = getattr(loop, "_linch_blocking_sem", None)
|
|
31
|
+
if sem is None:
|
|
32
|
+
sem = asyncio.Semaphore(_MAX_CONCURRENCY)
|
|
33
|
+
try:
|
|
34
|
+
loop._linch_blocking_sem = sem # type: ignore[attr-defined]
|
|
35
|
+
except Exception:
|
|
36
|
+
pass
|
|
37
|
+
return sem
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
async def run_blocking(fn: Callable[..., T], *args: Any, **kwargs: Any) -> T:
|
|
41
|
+
"""Run ``fn(*args, **kwargs)`` on a bounded daemon thread; return its result.
|
|
42
|
+
|
|
43
|
+
Propagates any exception ``fn`` raises to the awaiter. If the awaiting
|
|
44
|
+
coroutine is cancelled the daemon thread still runs to completion (the same
|
|
45
|
+
contract as ``asyncio.to_thread``), but it never blocks interpreter exit.
|
|
46
|
+
"""
|
|
47
|
+
loop = asyncio.get_running_loop()
|
|
48
|
+
sem = _loop_semaphore(loop)
|
|
49
|
+
async with sem:
|
|
50
|
+
fut: asyncio.Future[T] = loop.create_future()
|
|
51
|
+
|
|
52
|
+
def _target() -> None:
|
|
53
|
+
try:
|
|
54
|
+
value = fn(*args, **kwargs)
|
|
55
|
+
except BaseException as exc: # noqa: BLE001 - propagated to awaiter
|
|
56
|
+
loop.call_soon_threadsafe(_safe_set_exception, fut, exc)
|
|
57
|
+
return
|
|
58
|
+
loop.call_soon_threadsafe(_safe_set_result, fut, value)
|
|
59
|
+
|
|
60
|
+
threading.Thread(target=_target, name="linch-blocking", daemon=True).start()
|
|
61
|
+
return await fut
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _safe_set_result(fut: asyncio.Future[Any], value: Any) -> None:
|
|
65
|
+
if not fut.done():
|
|
66
|
+
fut.set_result(value)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _safe_set_exception(fut: asyncio.Future[Any], exc: BaseException) -> None:
|
|
70
|
+
if not fut.done():
|
|
71
|
+
fut.set_exception(exc)
|
linch/_http_errors.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from email.utils import parsedate_to_datetime
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def error_status(err: Exception) -> int | None:
|
|
9
|
+
value = getattr(err, "status_code", None) or getattr(err, "status", None)
|
|
10
|
+
return int(value) if isinstance(value, int) else None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def error_body(err: Exception) -> Any:
|
|
14
|
+
body = getattr(err, "body", None)
|
|
15
|
+
if body is not None:
|
|
16
|
+
return body
|
|
17
|
+
response = getattr(err, "response", None)
|
|
18
|
+
if response is not None:
|
|
19
|
+
body = getattr(response, "body", None)
|
|
20
|
+
if body is not None:
|
|
21
|
+
return body
|
|
22
|
+
try:
|
|
23
|
+
return response.json()
|
|
24
|
+
except Exception:
|
|
25
|
+
return None
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def nested_error(err: Exception) -> dict[str, Any]:
|
|
30
|
+
body = error_body(err)
|
|
31
|
+
if isinstance(body, dict):
|
|
32
|
+
raw = body.get("error", body)
|
|
33
|
+
if isinstance(raw, dict):
|
|
34
|
+
return raw
|
|
35
|
+
raw_error = getattr(err, "error", None)
|
|
36
|
+
if isinstance(raw_error, dict):
|
|
37
|
+
return raw_error
|
|
38
|
+
return {}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def error_code(err: Exception) -> str | None:
|
|
42
|
+
raw = nested_error(err).get("code") or nested_error(err).get("type")
|
|
43
|
+
return str(raw) if isinstance(raw, str) and raw else None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def error_message(err: Exception) -> str:
|
|
47
|
+
raw = nested_error(err).get("message")
|
|
48
|
+
if isinstance(raw, str) and raw:
|
|
49
|
+
return raw
|
|
50
|
+
return str(err)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def retry_after_seconds(err: Exception) -> float | None:
|
|
54
|
+
response = getattr(err, "response", None)
|
|
55
|
+
headers = getattr(response, "headers", None)
|
|
56
|
+
if not headers:
|
|
57
|
+
headers = getattr(err, "headers", None)
|
|
58
|
+
if not headers:
|
|
59
|
+
return None
|
|
60
|
+
raw = None
|
|
61
|
+
for key in ("retry-after", "Retry-After", "retry_after"):
|
|
62
|
+
try:
|
|
63
|
+
raw = headers.get(key)
|
|
64
|
+
except Exception:
|
|
65
|
+
raw = None
|
|
66
|
+
if raw is not None:
|
|
67
|
+
break
|
|
68
|
+
if raw is None:
|
|
69
|
+
return None
|
|
70
|
+
if isinstance(raw, (int, float)):
|
|
71
|
+
return max(0.0, float(raw))
|
|
72
|
+
text = str(raw).strip()
|
|
73
|
+
try:
|
|
74
|
+
return max(0.0, float(text))
|
|
75
|
+
except ValueError:
|
|
76
|
+
pass
|
|
77
|
+
try:
|
|
78
|
+
dt = parsedate_to_datetime(text)
|
|
79
|
+
except (TypeError, ValueError, IndexError, OverflowError):
|
|
80
|
+
return None
|
|
81
|
+
if dt.tzinfo is None:
|
|
82
|
+
dt = dt.replace(tzinfo=timezone.utc)
|
|
83
|
+
return max(0.0, (dt - datetime.now(timezone.utc)).total_seconds())
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def is_prompt_length_error(err: Exception) -> bool:
|
|
87
|
+
code = error_code(err)
|
|
88
|
+
if code == "context_length_exceeded":
|
|
89
|
+
return True
|
|
90
|
+
message = error_message(err).lower()
|
|
91
|
+
clear_phrases = (
|
|
92
|
+
"prompt is too long",
|
|
93
|
+
"maximum context length",
|
|
94
|
+
"context length exceeded",
|
|
95
|
+
"input is too long",
|
|
96
|
+
)
|
|
97
|
+
if any(phrase in message for phrase in clear_phrases):
|
|
98
|
+
return True
|
|
99
|
+
return "tokens" in message and "maximum" in message and "prompt" in message
|