drift-lang 0.1.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.
drift/__init__.py ADDED
@@ -0,0 +1,2 @@
1
+ """Drift — An intent-based programming language for agentic systems."""
2
+ __version__ = "0.1.0"
drift/ast_nodes.py ADDED
@@ -0,0 +1,522 @@
1
+ """
2
+ Drift AST Nodes — The tree structure that represents a parsed Drift program.
3
+
4
+ The pipeline: source → tokens → [PARSER] → AST → codegen → Python
5
+
6
+ Each node type here maps to a construct in the Drift language.
7
+ The code generator walks this tree and emits Python source code.
8
+ """
9
+
10
+ from dataclasses import dataclass, field
11
+ from typing import Optional
12
+
13
+
14
+ # ─── Top-Level Declarations ───────────────────────────────────────────
15
+
16
+ @dataclass
17
+ class Program:
18
+ """Root node: a .drift file is a list of declarations."""
19
+ declarations: list = field(default_factory=list)
20
+
21
+
22
+ @dataclass
23
+ class ImportDecl:
24
+ """import X[, Y] from "./other.drift" — names imported from another file.
25
+
26
+ For v0.2 the codegen emits a plain `from <basename>_drift import X, Y`.
27
+ Dependencies must be transpiled before the importer; the CLI handles this
28
+ automatically when running `drift run`/`drift transpile` on a tree.
29
+ """
30
+ names: list = field(default_factory=list) # list of str
31
+ source_path: str = "" # path string from the `from` clause
32
+
33
+
34
+ @dataclass
35
+ class ConfigDecl:
36
+ """config { name: "...", version: "..." }"""
37
+ entries: dict = field(default_factory=dict)
38
+
39
+
40
+ @dataclass
41
+ class SchemaDecl:
42
+ """schema FitScore { field: type [constraints] }"""
43
+ name: str = ""
44
+ fields: list = field(default_factory=list) # list of SchemaField
45
+
46
+
47
+ @dataclass
48
+ class SchemaField:
49
+ name: str = ""
50
+ type_expr: 'TypeExpr | None' = None
51
+ optional: bool = False
52
+ constraints: list = field(default_factory=list) # list of Constraint
53
+
54
+
55
+ @dataclass
56
+ class AgentDecl:
57
+ """agent GrantChecker { model: ..., budget: ..., steps... }"""
58
+ name: str = ""
59
+ model_config: 'ModelConfig | None' = None
60
+ budget_config: 'BudgetConfig | None' = None
61
+ quality_config: 'QualityConfig | None' = None
62
+ memory_config: 'MemoryConfig | None' = None
63
+ state_block: list = field(default_factory=list) # list of StateField
64
+ steps: list = field(default_factory=list) # list of StepDecl
65
+
66
+
67
+ @dataclass
68
+ class MemoryConfig:
69
+ """Memory configuration. Two forms:
70
+
71
+ memory: dendric("persona_name")
72
+ → backend="dendric", persona="persona_name". The codegen wires
73
+ this to DendricStore (with mock fallback when DATABASE_URL
74
+ is unset).
75
+
76
+ memory { store: "sqlite://...", recall strategy: "semantic", ... }
77
+ → legacy block form, backend="sqlite" (default).
78
+ """
79
+ backend: str = "sqlite" # "sqlite" | "dendric"
80
+ persona: str = "" # only used when backend=="dendric"
81
+ store: str = "sqlite://:memory:" # only used when backend=="sqlite"
82
+ recall_strategy: str = "recent" # "semantic" | "recent" | "relevant" | "all"
83
+ max_recall: int = 20
84
+ decay_enabled: bool = False
85
+
86
+
87
+ @dataclass
88
+ class StateField:
89
+ """A single line inside an agent's state block: name: type = default."""
90
+ name: str = ""
91
+ type_expr: 'TypeExpr | None' = None
92
+ default: 'Expression | None' = None
93
+
94
+
95
+ @dataclass
96
+ class PipelineDecl:
97
+ """A pipeline: a DAG of step calls across one or more agents.
98
+
99
+ Top-level declaration. See §2.5 and §12.1 of the spec.
100
+
101
+ `use_agents` lists agent type names that participate (`use GrantChecker`).
102
+ `edges` lists each `from -> to` flow with its operator.
103
+ `failure_handlers` map step names to recovery actions.
104
+ """
105
+ name: str = ""
106
+ budget_config: 'BudgetConfig | None' = None
107
+ timeout_seconds: float = 0.0
108
+ schedule: str = ""
109
+ use_agents: list = field(default_factory=list) # list of str
110
+ edges: list = field(default_factory=list) # list of PipelineEdge
111
+ failure_handlers: dict = field(default_factory=dict) # step_name -> action
112
+ budget_handler: str = "" # action on budget exceeded
113
+ inline_steps: list = field(default_factory=list) # list of StepDecl (defined inline)
114
+
115
+
116
+ @dataclass
117
+ class PipelineEdge:
118
+ """One arrow in a pipeline: from_node OP to_node
119
+
120
+ OP is "->" (seq), "=>" (parallel fan-out), "~>" (conditional), "|>" (stream).
121
+ Nodes are either "step" or "AgentType.step".
122
+ """
123
+ from_node: str = ""
124
+ to_node: str = ""
125
+ op: str = "->"
126
+
127
+
128
+ @dataclass
129
+ class ToolDecl:
130
+ """A tool declaration. Three forms — `kind` distinguishes them.
131
+
132
+ kind == "mcp": `tool name from mcp "url"`
133
+ kind == "python": `tool name from python "module.path:fn"`
134
+ kind == "rest": `tool name { endpoint: ..., auth: ..., action ... }`
135
+ """
136
+ name: str = ""
137
+ kind: str = "" # "mcp" | "python" | "rest"
138
+ source: str = "" # URL (mcp) or "module:fn" (python)
139
+ endpoint: str = "" # REST base URL
140
+ auth_env: str = "" # env var name for auth header
141
+ actions: list = field(default_factory=list) # list of ToolAction
142
+
143
+
144
+ @dataclass
145
+ class ToolAction:
146
+ """One action inside a REST tool.
147
+
148
+ action lookup(company_number: string) -> CompanyProfile {
149
+ GET "/company/{company_number}"
150
+ }
151
+ """
152
+ name: str = ""
153
+ params: list = field(default_factory=list) # list of Param
154
+ return_type: 'TypeExpr | None' = None
155
+ method: str = "GET" # GET, POST, ...
156
+ path: str = "" # URL template with {var}
157
+
158
+
159
+ @dataclass
160
+ class VerbDecl:
161
+ """define verb name { pattern: ..., prompt: ..., output: ..., temperature: ... }
162
+
163
+ Top-level declaration. Registers a custom intent verb that can be used
164
+ anywhere a built-in verb appears (classify, extract, summarize, ...).
165
+ """
166
+ name: str = ""
167
+ pattern: str = "" # cosmetic for v0.2 — docs only
168
+ prompt: str = "" # system prompt for the LLM
169
+ output: 'TypeExpr | None' = None
170
+ temperature: float = 0.0 # 0 = unspecified; default model temp
171
+
172
+
173
+ @dataclass
174
+ class StepDecl:
175
+ """step check(doc: Document) -> FitScore { ... }"""
176
+ name: str = ""
177
+ params: list = field(default_factory=list) # list of Param
178
+ return_type: 'TypeExpr | None' = None
179
+ body: list = field(default_factory=list) # list of Statement
180
+ modifier: str = "" # "cached", "parallel", "manual", "silent", ""
181
+
182
+
183
+ @dataclass
184
+ class Param:
185
+ name: str = ""
186
+ type_expr: 'TypeExpr | None' = None
187
+
188
+
189
+ # ─── Configuration Blocks ─────────────────────────────────────────────
190
+
191
+ @dataclass
192
+ class ModelConfig:
193
+ """Model routing configuration.
194
+
195
+ Routing modes:
196
+ "default" — single model, optionally with fallback list
197
+ "prefer" — preferred model + fallback chain
198
+ "stream_then" — temporal model routing: fire `stream_model` for an
199
+ instant bridge response while `then_model` does the
200
+ real reasoning, supersede the bridge when ready.
201
+ Used by the voice stack but the language feature
202
+ is provider-agnostic (works for any two models).
203
+ """
204
+ mode: str = "default" # "default" | "prefer" | "stream_then"
205
+ default: str = ""
206
+ prefer: str = ""
207
+ fallback: str = "" # comma-allowed string OR list, normalized in parser
208
+ fallback_list: list = field(default_factory=list)
209
+ never: str = ""
210
+ never_list: list = field(default_factory=list)
211
+ upgrades: list = field(default_factory=list) # list of ModelUpgrade
212
+ stream_model: str = "" # fast bridge model
213
+ then_model: str = "" # slow reasoning model
214
+
215
+
216
+ @dataclass
217
+ class ModelUpgrade:
218
+ """upgrade to "X" when { conditions... }"""
219
+ target_model: str = ""
220
+ conditions: list = field(default_factory=list) # list of UpgradeCondition
221
+
222
+
223
+ @dataclass
224
+ class UpgradeCondition:
225
+ """One condition inside an upgrade rule.
226
+
227
+ Forms supported in v0.2:
228
+ - `confidence < 0.8` (kind="confidence_lt", value=0.8) [parsed, unenforced]
229
+ - `input_tokens > N` (kind="tokens_gt", value=N)
230
+ - `step is <name>` (kind="step_is", value=name)
231
+ """
232
+ kind: str = ""
233
+ value: 'float | int | str' = 0
234
+
235
+
236
+ @dataclass
237
+ class BudgetConfig:
238
+ amount: str = "" # e.g. "£5"
239
+ currency_sym: str = "" # £, $, €
240
+ value: float = 0.0
241
+ per: str = "run" # "run", "day", "company"
242
+
243
+
244
+ @dataclass
245
+ class QualityConfig:
246
+ min_confidence: float = 0.85
247
+
248
+
249
+ # ─── Type Expressions ─────────────────────────────────────────────────
250
+
251
+ @dataclass
252
+ class TypeExpr:
253
+ """Base type expression."""
254
+ name: str = "" # "string", "number", "bool", "document", or a schema name
255
+
256
+
257
+ @dataclass
258
+ class ListType(TypeExpr):
259
+ element_type: 'TypeExpr | None' = None
260
+
261
+
262
+ @dataclass
263
+ class MapType(TypeExpr):
264
+ key_type: 'TypeExpr | None' = None
265
+ value_type: 'TypeExpr | None' = None
266
+
267
+
268
+ @dataclass
269
+ class ConfidentType(TypeExpr):
270
+ inner_type: 'TypeExpr | None' = None
271
+
272
+
273
+ @dataclass
274
+ class EnumType(TypeExpr):
275
+ """one of "a", "b", "c" """
276
+ values: list = field(default_factory=list)
277
+
278
+
279
+ # ─── Constraints ───────────────────────────────────────────────────────
280
+
281
+ @dataclass
282
+ class BetweenConstraint:
283
+ low: float = 0.0
284
+ high: float = 0.0
285
+
286
+
287
+ # ─── Statements ────────────────────────────────────────────────────────
288
+
289
+ @dataclass
290
+ class LetStmt:
291
+ name: str = ""
292
+ value: 'Expression' = None
293
+
294
+
295
+ @dataclass
296
+ class ReturnStmt:
297
+ value: 'Expression' = None
298
+
299
+
300
+ @dataclass
301
+ class RespondStmt:
302
+ value: 'Expression' = None
303
+
304
+
305
+ @dataclass
306
+ class IfStmt:
307
+ condition: 'Expression' = None
308
+ body: list = field(default_factory=list)
309
+ otherwise_body: list = field(default_factory=list)
310
+ otherwise_if: 'IfStmt | None' = None
311
+
312
+
313
+ @dataclass
314
+ class ForEachStmt:
315
+ var_name: str = ""
316
+ iterable: 'Expression' = None
317
+ body: list = field(default_factory=list)
318
+ parallel: bool = False
319
+
320
+
321
+ @dataclass
322
+ class MatchStmt:
323
+ target: 'Expression' = None
324
+ arms: list = field(default_factory=list) # list of MatchArm
325
+
326
+
327
+ @dataclass
328
+ class MatchArm:
329
+ pattern: 'Expression' = None # StringLit or Ident for "any other"
330
+ body: list = field(default_factory=list)
331
+ is_default: bool = False
332
+
333
+
334
+ @dataclass
335
+ class DejaVuStmt:
336
+ """deja_vu match on <context_expr> { "pattern_name" -> { body } ... }
337
+
338
+ Semantically: pass context_expr to memory.deja_vu_check(); when the
339
+ archive trigger fires, dispatch to the matching arm. The match's
340
+ pattern_type is compared substring-wise against each arm's pattern
341
+ string (v1 classification — see DendricStore.DejaVuMatch.matches)."""
342
+ context_expr: 'Expression' = None
343
+ arms: list = field(default_factory=list) # list of DejaVuArm
344
+
345
+
346
+ @dataclass
347
+ class DejaVuArm:
348
+ pattern: str = ""
349
+ body: list = field(default_factory=list)
350
+ is_default: bool = False
351
+
352
+
353
+ @dataclass
354
+ class ForgetStmt:
355
+ """forget memories tagged X / older than Nd / where temp < X
356
+
357
+ `mode` is one of: "by_tag" (tag expr), "by_age" (older_than_days int),
358
+ "by_temp" (below_temp float). All three route to the adapter's
359
+ forget() method — see DendricStore.forget."""
360
+ mode: str = "by_temp"
361
+ tag: 'Expression' = None
362
+ older_than_days: int = 0
363
+ below_temp: float = 0.0
364
+
365
+
366
+ @dataclass
367
+ class ExprStmt:
368
+ """An expression used as a statement (e.g., a function call)."""
369
+ expr: 'Expression' = None
370
+
371
+
372
+ @dataclass
373
+ class AttemptStmt:
374
+ """attempt { body } recover from { ErrorType -> handler ... }
375
+
376
+ Each arm matches an exception class by name. The arm body is a list of
377
+ statements; a single-statement arm can be inline via `-> statement`.
378
+ Special arm patterns:
379
+ - `any error` matches any DriftError (and is the default fallthrough).
380
+ - `retry` (as a single-statement body) re-runs the attempt block.
381
+ - `fail with "..."` raises StepFailed with the given message.
382
+ """
383
+ body: list = field(default_factory=list)
384
+ arms: list = field(default_factory=list) # list of RecoverArm
385
+ max_retries: int = 3
386
+
387
+
388
+ @dataclass
389
+ class RecoverArm:
390
+ error_type: str = "" # exception class name, or "any" for default
391
+ body: list = field(default_factory=list)
392
+ is_default: bool = False
393
+
394
+
395
+ @dataclass
396
+ class RetryStmt:
397
+ """`retry` inside a recover arm — re-run the attempt block."""
398
+ pass
399
+
400
+
401
+ @dataclass
402
+ class RecallStmt:
403
+ """recall [similar] <description> [for <key>] — wraps memory.recall().
404
+
405
+ Always an expression (returns a list). Parsed at expression position.
406
+ """
407
+ description: str = ""
408
+ key: 'Expression | None' = None
409
+
410
+
411
+ @dataclass
412
+ class RememberStmt:
413
+ """remember <expr> [tagged <key>] — wraps memory.remember()."""
414
+ value: 'Expression' = None
415
+ tag: 'Expression | None' = None
416
+
417
+
418
+ @dataclass
419
+ class FailStmt:
420
+ """`fail with "<message>"` — raise StepFailed."""
421
+ message: 'Expression' = None
422
+
423
+
424
+ # ─── Expressions ───────────────────────────────────────────────────────
425
+
426
+ @dataclass
427
+ class IntentExpr:
428
+ """classify doc as Category — the core Drift primitive."""
429
+ verb: str = "" # "classify", "extract", "summarize", etc.
430
+ input_expr: 'Expression' = None
431
+ clauses: dict = field(default_factory=dict)
432
+ # Possible clause keys: "as" (type), "from" (source), "in" (count),
433
+ # "against" (criteria), "to" (target), "using" (context),
434
+ # "considering" (factors)
435
+
436
+
437
+ @dataclass
438
+ class FnCall:
439
+ name: str = ""
440
+ args: list = field(default_factory=list)
441
+ # For method calls like results.add(x), target is set
442
+ target: 'Expression | None' = None
443
+
444
+
445
+ @dataclass
446
+ class FieldAccess:
447
+ target: 'Expression' = None
448
+ field_name: str = ""
449
+
450
+
451
+ @dataclass
452
+ class BinOp:
453
+ op: str = ""
454
+ left: 'Expression' = None
455
+ right: 'Expression' = None
456
+
457
+
458
+ @dataclass
459
+ class UnaryOp:
460
+ op: str = ""
461
+ operand: 'Expression' = None
462
+
463
+
464
+ @dataclass
465
+ class Ident:
466
+ name: str = ""
467
+
468
+
469
+ @dataclass
470
+ class StringLit:
471
+ value: str = ""
472
+ has_interpolation: bool = False
473
+
474
+
475
+ @dataclass
476
+ class NumberLit:
477
+ value: float = 0.0
478
+
479
+
480
+ @dataclass
481
+ class CurrencyLit:
482
+ symbol: str = ""
483
+ value: float = 0.0
484
+
485
+
486
+ @dataclass
487
+ class DurationLit:
488
+ value: float = 0.0
489
+ unit: str = "" # s, m, h, d
490
+
491
+
492
+ @dataclass
493
+ class BoolLit:
494
+ value: bool = False
495
+
496
+
497
+ @dataclass
498
+ class ListLit:
499
+ elements: list = field(default_factory=list)
500
+
501
+
502
+ @dataclass
503
+ class SchemaConstructor:
504
+ """FitScore { field: value, ... }"""
505
+ type_name: str = ""
506
+ fields: dict = field(default_factory=dict)
507
+
508
+
509
+ @dataclass
510
+ class PipeExpr:
511
+ """expr |> fn"""
512
+ input_expr: 'Expression' = None
513
+ operations: list = field(default_factory=list)
514
+
515
+
516
+ # Type alias for documentation
517
+ Expression = (IntentExpr | FnCall | FieldAccess | BinOp | Ident |
518
+ StringLit | NumberLit | CurrencyLit | DurationLit |
519
+ BoolLit | ListLit | SchemaConstructor | PipeExpr | UnaryOp)
520
+
521
+ Statement = (LetStmt | ReturnStmt | RespondStmt | IfStmt |
522
+ ForEachStmt | MatchStmt | DejaVuStmt | ForgetStmt | ExprStmt)