contextos-memory-runtime 1.0.0rc2__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.
Files changed (93) hide show
  1. contextos/__init__.py +3 -0
  2. contextos/__main__.py +6 -0
  3. contextos/api/__init__.py +1 -0
  4. contextos/api/routes/__init__.py +1 -0
  5. contextos/api/routes/desktop.py +322 -0
  6. contextos/api/routes/ingest.py +17 -0
  7. contextos/api/routes/memories.py +84 -0
  8. contextos/api/routes/models.py +81 -0
  9. contextos/api/routes/retrieval.py +89 -0
  10. contextos/api/routes/system.py +216 -0
  11. contextos/api/server.py +195 -0
  12. contextos/benchmarks/__init__.py +1 -0
  13. contextos/benchmarks/compilation.py +245 -0
  14. contextos/benchmarks/connectors.py +423 -0
  15. contextos/benchmarks/explainability.py +103 -0
  16. contextos/benchmarks/final.py +406 -0
  17. contextos/benchmarks/graph.py +310 -0
  18. contextos/benchmarks/graph_adversarial.py +525 -0
  19. contextos/benchmarks/mcp.py +324 -0
  20. contextos/benchmarks/model_routing.py +203 -0
  21. contextos/benchmarks/optimization.py +305 -0
  22. contextos/benchmarks/rescue_integration.py +127 -0
  23. contextos/benchmarks/retrieval.py +266 -0
  24. contextos/benchmarks/temporal.py +377 -0
  25. contextos/benchmarks/temporal_hotpath.py +76 -0
  26. contextos/benchmarks/terminal.py +62 -0
  27. contextos/cli/__init__.py +1 -0
  28. contextos/cli/app.py +932 -0
  29. contextos/cli/dashboard.py +174 -0
  30. contextos/cli/formatters.py +299 -0
  31. contextos/config/__init__.py +1 -0
  32. contextos/config/settings.py +160 -0
  33. contextos/connectors/__init__.py +6 -0
  34. contextos/connectors/fake.py +11 -0
  35. contextos/connectors/json_import.py +125 -0
  36. contextos/connectors/local_files.py +102 -0
  37. contextos/connectors/manager.py +293 -0
  38. contextos/connectors/models.py +62 -0
  39. contextos/connectors/protocols.py +11 -0
  40. contextos/core/__init__.py +103 -0
  41. contextos/core/enums.py +489 -0
  42. contextos/core/exceptions.py +293 -0
  43. contextos/core/models.py +1147 -0
  44. contextos/core/protocols.py +549 -0
  45. contextos/daemon/__init__.py +1 -0
  46. contextos/daemon/manager.py +510 -0
  47. contextos/daemon/state.py +127 -0
  48. contextos/daemon/wiring.py +296 -0
  49. contextos/demo.py +217 -0
  50. contextos/embedding/__init__.py +1 -0
  51. contextos/embedding/deterministic.py +76 -0
  52. contextos/embedding/sentence_transformers.py +80 -0
  53. contextos/mcp/__init__.py +5 -0
  54. contextos/mcp/server.py +269 -0
  55. contextos/providers/__init__.py +13 -0
  56. contextos/providers/fake.py +217 -0
  57. contextos/providers/ollama.py +297 -0
  58. contextos/providers/openai_compatible.py +337 -0
  59. contextos/services/__init__.py +1 -0
  60. contextos/services/compilation.py +535 -0
  61. contextos/services/explainability.py +553 -0
  62. contextos/services/extraction.py +311 -0
  63. contextos/services/graph.py +524 -0
  64. contextos/services/graph_retrieval.py +143 -0
  65. contextos/services/ingestion.py +143 -0
  66. contextos/services/inspection.py +174 -0
  67. contextos/services/memory.py +291 -0
  68. contextos/services/model_service.py +409 -0
  69. contextos/services/optimization.py +426 -0
  70. contextos/services/privacy.py +331 -0
  71. contextos/services/retrieval.py +302 -0
  72. contextos/services/retrieval_index.py +88 -0
  73. contextos/services/router.py +302 -0
  74. contextos/services/secret_scanner.py +207 -0
  75. contextos/services/telemetry_query.py +102 -0
  76. contextos/services/temporal.py +500 -0
  77. contextos/services/token_counter.py +222 -0
  78. contextos/storage/__init__.py +1 -0
  79. contextos/storage/connector_repo.py +67 -0
  80. contextos/storage/database.py +497 -0
  81. contextos/storage/event_repo.py +137 -0
  82. contextos/storage/graph_repo.py +228 -0
  83. contextos/storage/lexical/__init__.py +1 -0
  84. contextos/storage/lexical/bm25.py +134 -0
  85. contextos/storage/memory_repo.py +589 -0
  86. contextos/storage/relation_repo.py +80 -0
  87. contextos/storage/telemetry_repo.py +481 -0
  88. contextos/storage/vector/__init__.py +1 -0
  89. contextos/storage/vector/in_memory.py +162 -0
  90. contextos_memory_runtime-1.0.0rc2.dist-info/METADATA +143 -0
  91. contextos_memory_runtime-1.0.0rc2.dist-info/RECORD +93 -0
  92. contextos_memory_runtime-1.0.0rc2.dist-info/WHEEL +4 -0
  93. contextos_memory_runtime-1.0.0rc2.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,103 @@
1
+ """Core domain package for ContextOS.
2
+
3
+ Re-exports the most commonly used types for convenience.
4
+ """
5
+
6
+ from contextos.core.enums import (
7
+ CandidateAction,
8
+ CandidateTemporalStatus,
9
+ EventType,
10
+ GraphNodeType,
11
+ GraphRelationType,
12
+ MemoryStatus,
13
+ MemoryType,
14
+ PrivacyClassification,
15
+ PrivacyDecision,
16
+ PrivacyLevel,
17
+ PrivacySeverity,
18
+ RelationType,
19
+ SecretDetectionMode,
20
+ SecretType,
21
+ SourceRole,
22
+ SourceTrust,
23
+ )
24
+ from contextos.core.exceptions import (
25
+ ConcurrencyError,
26
+ ContextOSError,
27
+ InvalidTransitionError,
28
+ MemoryNotFoundError,
29
+ SecretDetectedError,
30
+ )
31
+ from contextos.core.models import (
32
+ CandidateMemory,
33
+ CompiledContext,
34
+ CompilationConfig,
35
+ ExtractedMemory,
36
+ GraphEdge,
37
+ GraphEdgeSupport,
38
+ GraphExpansion,
39
+ GraphNode,
40
+ GraphPath,
41
+ IngestRequest,
42
+ IngestResult,
43
+ Memory,
44
+ MemoryFilters,
45
+ MemoryRelation,
46
+ MemoryUpdate,
47
+ PrivacyAssessment,
48
+ PrivacyFinding,
49
+ RawEvent,
50
+ RetrievalConfig,
51
+ RetrievalResult,
52
+ ScanResult,
53
+ ScoredMemory,
54
+ )
55
+
56
+ __all__ = [
57
+ # Enums
58
+ "CandidateAction",
59
+ "CandidateTemporalStatus",
60
+ "EventType",
61
+ "GraphNodeType",
62
+ "GraphRelationType",
63
+ "MemoryStatus",
64
+ "MemoryType",
65
+ "PrivacyClassification",
66
+ "PrivacyDecision",
67
+ "PrivacyLevel",
68
+ "PrivacySeverity",
69
+ "RelationType",
70
+ "SecretDetectionMode",
71
+ "SecretType",
72
+ "SourceRole",
73
+ "SourceTrust",
74
+ # Exceptions
75
+ "ConcurrencyError",
76
+ "ContextOSError",
77
+ "InvalidTransitionError",
78
+ "MemoryNotFoundError",
79
+ "SecretDetectedError",
80
+ # Models
81
+ "CandidateMemory",
82
+ "CompiledContext",
83
+ "CompilationConfig",
84
+ "ExtractedMemory",
85
+ "GraphEdge",
86
+ "GraphEdgeSupport",
87
+ "GraphExpansion",
88
+ "GraphNode",
89
+ "GraphPath",
90
+ "IngestRequest",
91
+ "IngestResult",
92
+ "Memory",
93
+ "MemoryFilters",
94
+ "MemoryRelation",
95
+ "MemoryUpdate",
96
+ "PrivacyAssessment",
97
+ "PrivacyFinding",
98
+ "RawEvent",
99
+ "RetrievalConfig",
100
+ "RetrievalResult",
101
+ "ScanResult",
102
+ "ScoredMemory",
103
+ ]
@@ -0,0 +1,489 @@
1
+ """Core domain enumerations for ContextOS.
2
+
3
+ These enums define the controlled vocabularies used across the system.
4
+ Every enum value is a lowercase string for consistent serialization and storage.
5
+ """
6
+
7
+ from enum import StrEnum
8
+
9
+
10
+ class MemoryStatus(StrEnum):
11
+ """Lifecycle states for memories.
12
+
13
+ State machine transitions are enforced by MemoryService.transition().
14
+ See Phase 0 spec Section 8 for the full state diagram.
15
+ """
16
+
17
+ CANDIDATE = "candidate"
18
+ """Freshly extracted, not yet validated against existing memories."""
19
+
20
+ ACTIVE = "active"
21
+ """Validated, current, trusted memory. Retrievable and compilable."""
22
+
23
+ SUPERSEDED = "superseded"
24
+ """Replaced by a newer memory. Retains link to successor."""
25
+
26
+ CONTRADICTED = "contradicted"
27
+ """Conflicts with another memory. Pending resolution."""
28
+
29
+ EXPIRED = "expired"
30
+ """Past its relevance window (e.g., temporal memories)."""
31
+
32
+ HISTORICAL = "historical"
33
+ """Archived for provenance. Not actively retrieved."""
34
+
35
+ DELETED = "deleted"
36
+ """Soft-deleted. Not retrievable. Retained for audit/undo."""
37
+
38
+ PURGED = "purged"
39
+ """Hard-deleted. Data destroyed from all stores. Transient state."""
40
+
41
+ MERGED = "merged"
42
+ """Candidate was absorbed into an existing ACTIVE memory. Transient."""
43
+
44
+
45
+ # Valid state transitions: {from_state: {allowed_to_states}}
46
+ # Enforced at the service layer, not the enum layer.
47
+ VALID_TRANSITIONS: dict[MemoryStatus, set[MemoryStatus]] = {
48
+ MemoryStatus.CANDIDATE: {
49
+ MemoryStatus.ACTIVE,
50
+ MemoryStatus.MERGED,
51
+ MemoryStatus.CONTRADICTED,
52
+ },
53
+ MemoryStatus.ACTIVE: {
54
+ MemoryStatus.SUPERSEDED,
55
+ MemoryStatus.CONTRADICTED,
56
+ MemoryStatus.EXPIRED,
57
+ MemoryStatus.DELETED,
58
+ MemoryStatus.ACTIVE, # Reinforcement (confidence increase)
59
+ },
60
+ MemoryStatus.CONTRADICTED: {
61
+ MemoryStatus.ACTIVE,
62
+ MemoryStatus.SUPERSEDED,
63
+ MemoryStatus.DELETED,
64
+ },
65
+ MemoryStatus.SUPERSEDED: {
66
+ MemoryStatus.HISTORICAL,
67
+ },
68
+ MemoryStatus.EXPIRED: {
69
+ MemoryStatus.HISTORICAL,
70
+ },
71
+ MemoryStatus.HISTORICAL: {
72
+ MemoryStatus.DELETED,
73
+ },
74
+ MemoryStatus.DELETED: {
75
+ MemoryStatus.PURGED,
76
+ },
77
+ # Terminal states — no transitions out
78
+ MemoryStatus.PURGED: set(),
79
+ MemoryStatus.MERGED: set(),
80
+ }
81
+
82
+
83
+ class MemoryType(StrEnum):
84
+ """Categories of memories.
85
+
86
+ These are not mutually exclusive in reality, but each memory gets
87
+ exactly one primary type. This aids filtering and retrieval weighting.
88
+ """
89
+
90
+ PREFERENCE = "preference"
91
+ """User preference: 'I prefer dark mode', 'I like Python over Java'."""
92
+
93
+ FACT = "fact"
94
+ """Factual statement about the user: 'I work at Acme Corp'."""
95
+
96
+ SKILL = "skill"
97
+ """Skill or competency: 'I'm proficient in Rust'."""
98
+
99
+ PROJECT = "project"
100
+ """Project information: 'I'm building a CLI tool called foo'."""
101
+
102
+ RELATIONSHIP = "relationship"
103
+ """Interpersonal relationship: 'Alice is my tech lead'."""
104
+
105
+ PROCEDURE = "procedure"
106
+ """How-to or workflow: 'To deploy, I run make deploy'."""
107
+
108
+ OPINION = "opinion"
109
+ """Subjective judgment: 'I think ORMs are overengineered'."""
110
+
111
+ TEMPORAL = "temporal"
112
+ """Time-bound context: 'I'm on vacation next week'."""
113
+
114
+ GOAL = "goal"
115
+ """Aspiration or objective: 'I want to learn Kubernetes'."""
116
+
117
+ CONTEXT = "context"
118
+ """General contextual information that doesn't fit other categories."""
119
+
120
+
121
+ class RetrievalMode(StrEnum):
122
+ """Available retrieval strategies."""
123
+
124
+ LEXICAL = "lexical"
125
+ DENSE = "dense"
126
+ HYBRID = "hybrid"
127
+ GRAPH = "graph"
128
+ HYBRID_GRAPH = "hybrid_graph"
129
+
130
+
131
+ class TemporalScope(StrEnum):
132
+ """Lifecycle window considered by a retrieval query."""
133
+
134
+ CURRENT = "current"
135
+ HISTORICAL = "historical"
136
+ ALL = "all"
137
+
138
+
139
+ class OptimizationStrategy(StrEnum):
140
+ """Algorithms available for memory-context selection."""
141
+
142
+ TOP_RANK = "top_rank"
143
+ TOP_RANK_STOP = "top_rank"
144
+ """Explicit alias for the original stop-at-first-nonfit baseline."""
145
+
146
+ TOP_RANK_SKIP = "top_rank_skip"
147
+ GREEDY = "greedy"
148
+ CONTEXTOS = "contextos"
149
+
150
+
151
+ class ExclusionReason(StrEnum):
152
+ """Why an optimizer candidate was not selected."""
153
+
154
+ BUDGET_EXHAUSTED = "budget_exhausted"
155
+ DUPLICATE_ID = "duplicate_id"
156
+ INVALID_LIFECYCLE = "invalid_lifecycle"
157
+ LOW_RELEVANCE = "low_relevance"
158
+ OVERSIZED = "oversized"
159
+ REDUNDANT = "redundant"
160
+
161
+
162
+ class CompilationStrategy(StrEnum):
163
+ """Representation strategies used after memory selection."""
164
+
165
+ RAW_CONCAT = "raw_concat"
166
+ DEDUP_ONLY = "dedup_only"
167
+ CONTEXTOS_COMPILER = "contextos_compiler"
168
+
169
+
170
+ class CompressionLevel(StrEnum):
171
+ """Amount of deterministic, extractive fact reduction."""
172
+
173
+ NONE = "none"
174
+ LIGHT = "light"
175
+ AGGRESSIVE = "aggressive"
176
+
177
+
178
+ class FactExclusionReason(StrEnum):
179
+ """Why a supported source fact was not emitted."""
180
+
181
+ BUDGET = "budget"
182
+ DUPLICATE = "duplicate"
183
+ EMPTY = "empty"
184
+ PRIVACY_RESTRICTED = "privacy_restricted"
185
+ QUERY_IRRELEVANT = "query_irrelevant"
186
+
187
+
188
+ class CompilerInputKind(StrEnum):
189
+ """How a memory reached the fact compiler."""
190
+
191
+ NORMAL_SELECTED = "normal_selected"
192
+ OVERSIZED_RESCUE = "oversized_rescue"
193
+
194
+
195
+ class CandidateTemporalStatus(StrEnum):
196
+ """Temporal interpretation inferred during candidate extraction."""
197
+
198
+ CURRENT = "current"
199
+ HISTORICAL = "historical"
200
+ FUTURE = "future"
201
+ UNSPECIFIED = "unspecified"
202
+
203
+
204
+ class TemporalPrecision(StrEnum):
205
+ """Precision of a claimed effective time without false timestamp accuracy."""
206
+
207
+ EXACT = "exact"
208
+ DATE = "date"
209
+ MONTH = "month"
210
+ YEAR = "year"
211
+ RELATIVE = "relative"
212
+ UNKNOWN = "unknown"
213
+
214
+
215
+ class TemporalOutcome(StrEnum):
216
+ """Deterministic result of resolving an accepted memory candidate."""
217
+
218
+ ADD_NEW = "add_new"
219
+ DUPLICATE = "duplicate"
220
+ COEXIST = "coexist"
221
+ SUPERSEDE = "supersede"
222
+ CONTRADICT = "contradict"
223
+ CORRECT = "correct"
224
+ NO_CHANGE = "no_change"
225
+
226
+
227
+ class CandidateAction(StrEnum):
228
+ """Non-binding hint for a later candidate validation stage."""
229
+
230
+ ADD = "add"
231
+ SUPERSEDE = "supersede"
232
+
233
+
234
+ class SourceRole(StrEnum):
235
+ """Who authored the text presented to the extractor."""
236
+
237
+ USER = "user"
238
+ ASSISTANT = "assistant"
239
+ SYSTEM = "system"
240
+
241
+
242
+ class PrivacyLevel(StrEnum):
243
+ """Privacy classification for memories.
244
+
245
+ Controls whether a memory can be sent to external LLM providers.
246
+ """
247
+
248
+ PUBLIC = "public"
249
+ """Can be sent to any LLM. No personal information."""
250
+
251
+ PERSONAL = "personal"
252
+ """Personal data. Can be sent to LLM but is PII-adjacent."""
253
+
254
+ SENSITIVE = "sensitive"
255
+ """Only sent with explicit user consent per query."""
256
+
257
+ RESTRICTED = "restricted"
258
+ """Never sent externally. Local retrieval only."""
259
+
260
+
261
+ class EventType(StrEnum):
262
+ """Types of events in the append-only event store."""
263
+
264
+ INGEST = "ingest"
265
+ """User ingested content into the system."""
266
+
267
+ MEMORY_CREATED = "memory_created"
268
+ """A new memory was extracted and stored."""
269
+
270
+ MEMORY_UPDATED = "memory_updated"
271
+ """Memory content or metadata was modified."""
272
+
273
+ MEMORY_TRANSITION = "memory_transition"
274
+ """Memory lifecycle state changed."""
275
+
276
+ MEMORY_DELETED = "memory_deleted"
277
+ """Memory was soft-deleted."""
278
+
279
+ MEMORY_PURGED = "memory_purged"
280
+ """Memory was hard-deleted (irreversible)."""
281
+
282
+ SECRET_DETECTED = "secret_detected"
283
+ """Secret scanner found sensitive content in input."""
284
+
285
+ CONFLICT_DETECTED = "conflict_detected"
286
+ """Duplicate or contradiction detected during ingestion."""
287
+
288
+ CONFLICT_RESOLVED = "conflict_resolved"
289
+ """A previously detected conflict was resolved."""
290
+
291
+ RETRIEVAL = "retrieval"
292
+ """A retrieval query was executed."""
293
+
294
+ COMPILATION = "compilation"
295
+ """Context was compiled for LLM consumption."""
296
+
297
+
298
+ class RelationType(StrEnum):
299
+ """Types of relationships between memories."""
300
+
301
+ SUPERSEDES = "supersedes"
302
+ """Source memory replaces target memory."""
303
+
304
+ CONTRADICTS = "contradicts"
305
+ """Source memory conflicts with target memory."""
306
+
307
+ CORRECTS = "corrects"
308
+ """Source memory explicitly corrects target memory."""
309
+
310
+ COEXISTS_WITH = "coexists_with"
311
+ """Memories share a property but apply to compatible scopes."""
312
+
313
+ DUPLICATE_OF = "duplicate_of"
314
+ """Source candidate repeats an existing memory without changing state."""
315
+
316
+ SUPPORTS = "supports"
317
+ """Source memory reinforces target memory."""
318
+
319
+ RELATED = "related"
320
+ """Topical or contextual relation."""
321
+
322
+ DERIVED_FROM = "derived_from"
323
+ """Source memory was extracted/summarized from target."""
324
+
325
+ PART_OF = "part_of"
326
+ """Source memory is a component of target."""
327
+
328
+
329
+ class GraphNodeType(StrEnum):
330
+ """Node kinds stored in the rebuildable memory graph."""
331
+
332
+ MEMORY = "memory"
333
+ PROJECT = "project"
334
+ TOOL = "tool"
335
+ CONCEPT = "concept"
336
+
337
+
338
+ class GraphRelationType(StrEnum):
339
+ """Conservative edge vocabulary for explicit graph facts."""
340
+
341
+ ABOUT = "about"
342
+ MENTIONS = "mentions"
343
+ USES = "uses"
344
+ RUNS = "runs"
345
+ DEPENDS_ON = "depends_on"
346
+ PART_OF = "part_of"
347
+ BELONGS_TO = "belongs_to"
348
+ WORKS_ON = "works_on"
349
+ SUPERSEDES = "supersedes"
350
+ CONTRADICTS = "contradicts"
351
+ CORRECTS = "corrects"
352
+ COEXISTS_WITH = "coexists_with"
353
+ DUPLICATE_OF = "duplicate_of"
354
+ SUPPORTS = "supports"
355
+ RELATED = "related"
356
+ DERIVED_FROM = "derived_from"
357
+
358
+
359
+ class SecretDetectionMode(StrEnum):
360
+ """How the secret scanner handles detected secrets."""
361
+
362
+ STRICT = "strict"
363
+ """Reject the entire input if any secret is detected."""
364
+
365
+ REDACT = "redact"
366
+ """Replace detected secrets with [REDACTED:type] placeholders."""
367
+
368
+ WARN = "warn"
369
+ """Store with warning flag. Memory marked as RESTRICTED."""
370
+
371
+
372
+ class SecretType(StrEnum):
373
+ """Types of secrets the scanner can detect."""
374
+
375
+ AWS_ACCESS_KEY = "aws_access_key"
376
+ AWS_SECRET_KEY = "aws_secret_key"
377
+ GITHUB_TOKEN = "github_token"
378
+ OPENAI_API_KEY = "openai_api_key"
379
+ ANTHROPIC_API_KEY = "anthropic_api_key"
380
+ GENERIC_API_KEY = "generic_api_key"
381
+ PRIVATE_KEY = "private_key"
382
+ SSH_PRIVATE_KEY = "ssh_private_key"
383
+ PASSWORD = "password"
384
+ CONNECTION_STRING = "connection_string"
385
+ JWT = "jwt"
386
+ HIGH_ENTROPY = "high_entropy"
387
+ GOOGLE_API_KEY = "google_api_key"
388
+ SLACK_TOKEN = "slack_token"
389
+ STRIPE_KEY = "stripe_key"
390
+ BEARER_TOKEN = "bearer_token"
391
+ AUTHORIZATION_HEADER = "authorization_header"
392
+ OTP = "otp"
393
+ SESSION_COOKIE = "session_cookie"
394
+ ACCESS_TOKEN = "access_token"
395
+
396
+
397
+ class PrivacySeverity(StrEnum):
398
+ LOW = "low"
399
+ MEDIUM = "medium"
400
+ HIGH = "high"
401
+ CRITICAL = "critical"
402
+
403
+
404
+ class PrivacyDecision(StrEnum):
405
+ ALLOW = "allow"
406
+ REDACT = "redact"
407
+ REJECT = "reject"
408
+ QUARANTINE = "quarantine"
409
+
410
+
411
+ class PrivacyClassification(StrEnum):
412
+ SAFE = "safe"
413
+ SENSITIVE = "sensitive"
414
+ SECRET = "secret"
415
+ BLOCKED = "blocked"
416
+
417
+
418
+ class SourceTrust(StrEnum):
419
+ DIRECT_USER = "direct_user"
420
+ LOCAL_TRUSTED_CONNECTOR = "local_trusted_connector"
421
+ EXTERNAL_WEBPAGE = "external_webpage"
422
+ IMPORTED_DOCUMENT = "imported_document"
423
+ TOOL_OUTPUT = "tool_output"
424
+ MODEL_OUTPUT = "model_output"
425
+
426
+
427
+ # ---------------------------------------------------------------------------
428
+ # Phase 9: Model Runtime, Routing, and Telemetry
429
+ # ---------------------------------------------------------------------------
430
+
431
+
432
+ class RoutingPolicy(StrEnum):
433
+ """Policies guiding deterministic model routing decisions."""
434
+
435
+ EXPLICIT = "explicit"
436
+ """Target provider and/or model explicitly requested."""
437
+
438
+ LOCAL_FIRST = "local_first"
439
+ """Prefer healthy local runtime; fallback to cloud only if configured."""
440
+
441
+ FIXED_DEFAULT = "fixed_default"
442
+ """Route strictly to the configured default provider/model."""
443
+
444
+ CAPABILITY_AWARE = "capability_aware"
445
+ """Select model meeting required capabilities and context window fit."""
446
+
447
+
448
+ class TokenMeasurementSource(StrEnum):
449
+ """Provenance/measurement method for token counts."""
450
+
451
+ PROVIDER_REPORTED = "provider_reported"
452
+ """Exact usage returned in the provider's API response."""
453
+
454
+ TOKENIZER_COUNTED = "tokenizer_counted"
455
+ """Locally calculated via exact or target-model tokenizer."""
456
+
457
+ APPROXIMATED = "approximated"
458
+ """Calculated via deterministic heuristic/approximation."""
459
+
460
+
461
+ class ModelFinishReason(StrEnum):
462
+ """Reason why downstream generation completed."""
463
+
464
+ STOP = "stop"
465
+ LENGTH = "length"
466
+ TIMEOUT = "timeout"
467
+ ERROR = "error"
468
+ CONTENT_FILTER = "content_filter"
469
+ UNKNOWN = "unknown"
470
+
471
+
472
+ class ProviderType(StrEnum):
473
+ """Supported provider adapter classes."""
474
+
475
+ FAKE = "fake"
476
+ OLLAMA = "ollama"
477
+ OPENAI_COMPATIBLE = "openai_compatible"
478
+ OPENAI = "openai"
479
+ ANTHROPIC = "anthropic"
480
+
481
+
482
+ class ProviderDispatchState(StrEnum):
483
+ """Observable lifecycle state of a downstream model provider dispatch."""
484
+
485
+ NOT_ATTEMPTED = "NOT_ATTEMPTED"
486
+ REQUEST_CONSTRUCTED = "REQUEST_CONSTRUCTED"
487
+ DISPATCH_ATTEMPTED = "DISPATCH_ATTEMPTED"
488
+ RESPONSE_RECEIVED = "RESPONSE_RECEIVED"
489
+ DISPATCH_FAILED = "DISPATCH_FAILED"