struckdown 0.3.11__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.
Files changed (107) hide show
  1. struckdown-0.3.11/PKG-INFO +561 -0
  2. struckdown-0.3.11/README.md +522 -0
  3. struckdown-0.3.11/pyproject.toml +70 -0
  4. struckdown-0.3.11/setup.cfg +4 -0
  5. struckdown-0.3.11/struckdown/__init__.py +781 -0
  6. struckdown-0.3.11/struckdown/actions/__init__.py +937 -0
  7. struckdown-0.3.11/struckdown/actions/break_.py +18 -0
  8. struckdown-0.3.11/struckdown/actions/evidence.py +201 -0
  9. struckdown-0.3.11/struckdown/actions/fetch.py +48 -0
  10. struckdown-0.3.11/struckdown/actions/history.py +121 -0
  11. struckdown-0.3.11/struckdown/actions/markdownify.py +29 -0
  12. struckdown-0.3.11/struckdown/actions/search.py +206 -0
  13. struckdown-0.3.11/struckdown/actions/set_.py +16 -0
  14. struckdown-0.3.11/struckdown/actions/timestamp.py +27 -0
  15. struckdown-0.3.11/struckdown/cache.py +196 -0
  16. struckdown-0.3.11/struckdown/data/README.md +1 -0
  17. struckdown-0.3.11/struckdown/data/butter_robot.txt +45 -0
  18. struckdown-0.3.11/struckdown/data/meeseeks_box.txt +27 -0
  19. struckdown-0.3.11/struckdown/data/microverse_battery.txt +23 -0
  20. struckdown-0.3.11/struckdown/data/plumbus.txt +14 -0
  21. struckdown-0.3.11/struckdown/data/portal_gun.txt +32 -0
  22. struckdown-0.3.11/struckdown/errors.py +113 -0
  23. struckdown-0.3.11/struckdown/execution.py +116 -0
  24. struckdown-0.3.11/struckdown/grammar.lark +145 -0
  25. struckdown-0.3.11/struckdown/highlight.py +580 -0
  26. struckdown-0.3.11/struckdown/incremental.py +67 -0
  27. struckdown-0.3.11/struckdown/jinja_analysis.py +193 -0
  28. struckdown-0.3.11/struckdown/jinja_utils.py +233 -0
  29. struckdown-0.3.11/struckdown/llm.py +517 -0
  30. struckdown-0.3.11/struckdown/output_formatters.py +227 -0
  31. struckdown-0.3.11/struckdown/parsing.py +1828 -0
  32. struckdown-0.3.11/struckdown/playground/__init__.py +18 -0
  33. struckdown-0.3.11/struckdown/playground/chunking.py +164 -0
  34. struckdown-0.3.11/struckdown/playground/core.py +544 -0
  35. struckdown-0.3.11/struckdown/playground/evidence_cache.py +244 -0
  36. struckdown-0.3.11/struckdown/playground/flask_app.py +1799 -0
  37. struckdown-0.3.11/struckdown/playground/prompt_cache.py +239 -0
  38. struckdown-0.3.11/struckdown/playground/static/css/codemirror-struckdown.css +101 -0
  39. struckdown-0.3.11/struckdown/playground/static/css/playground.css +776 -0
  40. struckdown-0.3.11/struckdown/playground/static/js/batch-stream.js +475 -0
  41. struckdown-0.3.11/struckdown/playground/static/js/codemirror-struckdown.js +307 -0
  42. struckdown-0.3.11/struckdown/playground/static/js/editor.js +2484 -0
  43. struckdown-0.3.11/struckdown/playground/task_cache.py +323 -0
  44. struckdown-0.3.11/struckdown/playground/templates/base.html +49 -0
  45. struckdown-0.3.11/struckdown/playground/templates/editor.html +827 -0
  46. struckdown-0.3.11/struckdown/playground/templates/partials/inputs_panel.html +27 -0
  47. struckdown-0.3.11/struckdown/playground/templates/partials/outputs_batch.html +32 -0
  48. struckdown-0.3.11/struckdown/playground/templates/partials/outputs_single.html +39 -0
  49. struckdown-0.3.11/struckdown/playground/upload_cache.py +202 -0
  50. struckdown-0.3.11/struckdown/quarto/sd-highlight/_extension.yml +6 -0
  51. struckdown-0.3.11/struckdown/quarto/sd-highlight/highlight_fragment.py +225 -0
  52. struckdown-0.3.11/struckdown/quarto/sd-highlight/sd-highlight.css +144 -0
  53. struckdown-0.3.11/struckdown/quarto/sd-highlight/sd-highlight.lua +40 -0
  54. struckdown-0.3.11/struckdown/response_types.py +119 -0
  55. struckdown-0.3.11/struckdown/results.py +535 -0
  56. struckdown-0.3.11/struckdown/return_type_models.py +1172 -0
  57. struckdown-0.3.11/struckdown/sd_cli.py +2133 -0
  58. struckdown-0.3.11/struckdown/segment_processor.py +747 -0
  59. struckdown-0.3.11/struckdown/simpletest.py +5 -0
  60. struckdown-0.3.11/struckdown/static/mermaid.min.js +2029 -0
  61. struckdown-0.3.11/struckdown/stats.py +568 -0
  62. struckdown-0.3.11/struckdown/temporal_patterns.py +176 -0
  63. struckdown-0.3.11/struckdown/tests/__init__.py +1 -0
  64. struckdown-0.3.11/struckdown/tests/test_actions.py +519 -0
  65. struckdown-0.3.11/struckdown/tests/test_auto_escaping.py +505 -0
  66. struckdown-0.3.11/struckdown/tests/test_block_comments.py +171 -0
  67. struckdown-0.3.11/struckdown/tests/test_cache.py +72 -0
  68. struckdown-0.3.11/struckdown/tests/test_cache_integration.py +473 -0
  69. struckdown-0.3.11/struckdown/tests/test_chatter_simple.py +456 -0
  70. struckdown-0.3.11/struckdown/tests/test_evidence_action.py +357 -0
  71. struckdown-0.3.11/struckdown/tests/test_incremental.py +247 -0
  72. struckdown-0.3.11/struckdown/tests/test_jinja_conditionals.py +478 -0
  73. struckdown-0.3.11/struckdown/tests/test_llm_calls_parallel.py +234 -0
  74. struckdown-0.3.11/struckdown/tests/test_llm_response_injection.py +603 -0
  75. struckdown-0.3.11/struckdown/tests/test_optional_completion.py +241 -0
  76. struckdown-0.3.11/struckdown/tests/test_optional_pick.py +108 -0
  77. struckdown-0.3.11/struckdown/tests/test_options.py +143 -0
  78. struckdown-0.3.11/struckdown/tests/test_playground_core.py +280 -0
  79. struckdown-0.3.11/struckdown/tests/test_playground_e2e.py +665 -0
  80. struckdown-0.3.11/struckdown/tests/test_playground_flask.py +470 -0
  81. struckdown-0.3.11/struckdown/tests/test_prompt_cache.py +226 -0
  82. struckdown-0.3.11/struckdown/tests/test_prompt_injection.py +183 -0
  83. struckdown-0.3.11/struckdown/tests/test_quantifiers.py +319 -0
  84. struckdown-0.3.11/struckdown/tests/test_search_action.py +214 -0
  85. struckdown-0.3.11/struckdown/tests/test_stats.py +374 -0
  86. struckdown-0.3.11/struckdown/tests/test_system_messages.py +264 -0
  87. struckdown-0.3.11/struckdown/tests/test_task_cache.py +170 -0
  88. struckdown-0.3.11/struckdown/tests/test_temperature_overrides.py +171 -0
  89. struckdown-0.3.11/struckdown/tests/test_temporal_types.py +504 -0
  90. struckdown-0.3.11/struckdown/tests/test_together.py +660 -0
  91. struckdown-0.3.11/struckdown/tests/test_type_loader.py +469 -0
  92. struckdown-0.3.11/struckdown/tests/test_upload_cache.py +214 -0
  93. struckdown-0.3.11/struckdown/type_loader.py +703 -0
  94. struckdown-0.3.11/struckdown/types/extract.yaml +13 -0
  95. struckdown-0.3.11/struckdown/types/poem.yaml +13 -0
  96. struckdown-0.3.11/struckdown/types/product.yaml +21 -0
  97. struckdown-0.3.11/struckdown/types/speak.yaml +12 -0
  98. struckdown-0.3.11/struckdown/types/superhero.yaml +17 -0
  99. struckdown-0.3.11/struckdown/types/think.yaml +13 -0
  100. struckdown-0.3.11/struckdown/validation.py +251 -0
  101. struckdown-0.3.11/struckdown/visualize.py +992 -0
  102. struckdown-0.3.11/struckdown.egg-info/PKG-INFO +561 -0
  103. struckdown-0.3.11/struckdown.egg-info/SOURCES.txt +105 -0
  104. struckdown-0.3.11/struckdown.egg-info/dependency_links.txt +1 -0
  105. struckdown-0.3.11/struckdown.egg-info/entry_points.txt +2 -0
  106. struckdown-0.3.11/struckdown.egg-info/requires.txt +33 -0
  107. struckdown-0.3.11/struckdown.egg-info/top_level.txt +1 -0
@@ -0,0 +1,561 @@
1
+ Metadata-Version: 2.4
2
+ Name: struckdown
3
+ Version: 0.3.11
4
+ Summary: struckdown: markdown-like syntax for structured conversations with language models
5
+ Author-email: Ben Whalley <benwhalley@gmail.com>
6
+ Requires-Python: >=3.12
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: asgiref>=3.7.0
9
+ Requires-Dist: instructor[litellm]>=1.11.0
10
+ Requires-Dist: jinja2>=3.1.6
11
+ Requires-Dist: lark>=1.2.2
12
+ Requires-Dist: pydantic>=2.11.7
13
+ Requires-Dist: python-box>=7.3.2
14
+ Requires-Dist: python-decouple>=3.8
15
+ Requires-Dist: typer>=0.16.0
16
+ Requires-Dist: more-itertools>=10.7.0
17
+ Requires-Dist: jinja-markdown>=1.210911
18
+ Requires-Dist: pytest>=8.4.2
19
+ Requires-Dist: joblib>=1.3.0
20
+ Requires-Dist: dateutils>=0.6.12
21
+ Requires-Dist: openpyxl>=3.1.0
22
+ Requires-Dist: pandas>=2.0.0
23
+ Requires-Dist: rich>=13.0.0
24
+ Requires-Dist: requests>=2.28.0
25
+ Requires-Dist: readability-lxml>=0.8.1
26
+ Requires-Dist: markdownify>=0.14.1
27
+ Requires-Dist: validators>=0.35.0
28
+ Requires-Dist: ddgs>=8.0.0
29
+ Requires-Dist: flask>=3.0.0
30
+ Requires-Dist: flask-limiter>=3.5.0
31
+ Requires-Dist: pytest-xdist>=3.8.0
32
+ Requires-Dist: gunicorn>=21.0.0
33
+ Requires-Dist: filelock>=3.12.0
34
+ Requires-Dist: rank-bm25>=0.2.2
35
+ Provides-Extra: playwright
36
+ Requires-Dist: playwright>=1.40.0; extra == "playwright"
37
+ Provides-Extra: local
38
+ Requires-Dist: sentence-transformers>=2.2.0; extra == "local"
39
+
40
+ # struckdown
41
+
42
+ Markdown-based syntax for ***structured*** conversations with language models.
43
+
44
+
45
+ ## QuickStart
46
+
47
+ ```bash
48
+ # Install
49
+ uv tool install git+https://github.com/benwhalley/struckdown
50
+
51
+ # Configure
52
+ export LLM_API_KEY="sk-..."
53
+ export LLM_API_BASE="https://api.openai.com/v1"
54
+
55
+ # Try it
56
+ sd chat "Tell me a joke: [[joke]]"
57
+ ```
58
+
59
+
60
+ # Using a prompt file with actions
61
+
62
+ ```
63
+ # sets a config variable to run a web search for "oranges" and
64
+ # summarise the results
65
+ sd chat "[[@search|oranges]] Provide a 2-3 sentence summary [[summary]]"
66
+ ```
67
+
68
+ **[→ Full QuickStart Guide](docs/QUICKSTART.md)**
69
+
70
+ ## What is Struckdown?
71
+
72
+ Struckdown makes it easy to extract structured data from text using LLMs with a simple, markdown-inspired syntax.
73
+
74
+ ### Example: Batch Processing
75
+
76
+ Imagine you have unstructured data stored in free text. You can make it structured like this:
77
+
78
+ ```bash
79
+ % sd batch *.txt "Purpose, <5 words: [[purpose]]"
80
+ [
81
+ {
82
+ "filename": "butter_robot.txt",
83
+ "purpose": "Pass butter, question existence."
84
+ },
85
+ {
86
+ "filename": "plumbus.txt",
87
+ "purpose": "Household universal utility device."
88
+ },
89
+ {
90
+ "filename": "portal_gun.txt",
91
+ "purpose": "Interdimensional travel device."
92
+ }
93
+ ]
94
+ ```
95
+
96
+ ### Example: Type Extraction
97
+
98
+ Extract structured data with type constraints:
99
+
100
+ ```bash
101
+ % sd batch *.txt "Price: [[number:price]] Currency? [[pick:currency|schmeckles,brapples,flurbos]]"
102
+ [
103
+ {
104
+ "filename": "butter_robot.txt",
105
+ "price": 18,
106
+ "currency": "schmeckles"
107
+ },
108
+ {
109
+ "filename": "plumbus.txt",
110
+ "price": 6.5,
111
+ "currency": "brapples"
112
+ }
113
+ ]
114
+ ```
115
+
116
+ ### Example: Chaining Operations
117
+
118
+ Batch operations accept JSON, so you can chain commands:
119
+
120
+ ```bash
121
+ % sd batch *.txt "Purpose: [[purpose]] Name: [[name]]" | \
122
+ sd batch "Most similar on Amazon: [[product]]" -k
123
+ ```
124
+
125
+ ## Key Features
126
+
127
+ - **Simple syntax** -- `[[variable]]` for completions, `{{variable}}` for references
128
+ - **System messages** -- Control LLM behavior with `<system>` tags
129
+ - **Type safety** -- Extract booleans, numbers, dates, or pick from options
130
+ - **Token management** -- Use `<checkpoint>` to save tokens between steps
131
+ - **Batch processing** -- Process hundreds of files with progress bars
132
+ - **Caching** -- Automatic disk caching saves money and time
133
+ - **Custom actions** -- Extend with Python functions (RAG, APIs, databases)
134
+ - **Multiple outputs** -- JSON, CSV, Excel, or stdout
135
+ - **Web search and URL fetching** -- Extract data directly from web pages
136
+
137
+
138
+
139
+ ### Command Line
140
+
141
+ ```bash
142
+ # Extract product data from a web page
143
+ sd chat "{{source}} Extract the product name and price [[product:data]]" \
144
+ -s https://www.example.com/product/12345
145
+
146
+ # Fetch raw HTML (no readability processing)
147
+ sd chat "{{source}} Analyze the HTML structure [[analysis]]" \
148
+ -s https://example.com --raw
149
+ ```
150
+
151
+ ### In Templates (for Batch Processing)
152
+
153
+ Use the `@fetch` action to fetch URLs dynamically within templates:
154
+
155
+ ```
156
+ [[@fetch:page_content|product_url]]
157
+
158
+ Based on this product page:
159
+ {{page_content}}
160
+
161
+ Extract:
162
+ - Product name: [[name]]
163
+ - Price: [[number:price]]
164
+ ```
165
+
166
+ With an input spreadsheet containing a `product_url` column:
167
+
168
+ ```bash
169
+ sd batch products.xlsx template.sd -o results.xlsx
170
+ ```
171
+
172
+ Each row's URL will be fetched, processed with readability to extract the main content, and converted to markdown before being passed to the LLM.
173
+
174
+ #### @fetch Parameters
175
+
176
+ | Parameter | Default | Description |
177
+ |-----------|---------|-------------|
178
+ | `url` | required | URL to fetch (unquoted = variable, quoted = literal) |
179
+ | `raw` | `false` | Return raw HTML instead of markdown |
180
+ | `timeout` | `30` | Request timeout in seconds |
181
+ | `max_chars` | `32000` | Max characters (0 = no limit) |
182
+
183
+ Example with parameters:
184
+ ```
185
+ [[@fetch:content|url,raw=true,timeout=60,max_chars=0]]
186
+ ```
187
+
188
+ ## Documentation
189
+
190
+ ### Getting Started
191
+ - **[QuickStart](docs/QUICKSTART.md)** -- Get started in 5 minutes
192
+ - **[CLI Usage](docs/CLI_USAGE.md)** -- Complete command reference
193
+
194
+ ### Tutorials
195
+ - **[Building a RAG System](docs/TUTORIAL_RAG.md)** -- Extract → Search → Generate pattern
196
+ - **[Custom Actions](docs/CUSTOM_ACTIONS.md)** -- Extend with Python plugins
197
+
198
+ ### Reference
199
+ - **[Examples](examples/)** -- Real-world examples and test cases
200
+ - **[Security](SECURITY.md)** -- Security guidelines and best practices
201
+
202
+ ## Installation
203
+
204
+ Requires [UV](https://docs.astral.sh/uv/):
205
+
206
+ ```bash
207
+ # Install as a tool (recommended)
208
+ uv tool install git+https://github.com/benwhalley/struckdown
209
+
210
+ # Or install in current environment
211
+ uv pip install git+https://github.com/benwhalley/struckdown
212
+ ```
213
+
214
+ ## Configuration
215
+
216
+ Set these environment variables:
217
+
218
+ ```bash
219
+ export LLM_API_KEY="sk-..." # Your API key
220
+ export LLM_API_BASE="https://api.openai.com/v1" # API endpoint
221
+ export DEFAULT_LLM="gpt-4o-mini" # Model name
222
+ ```
223
+
224
+ ### VSCode Extension
225
+
226
+ Syntax highlighting for `.sd` files:
227
+
228
+ ```bash
229
+ cd vscode-extension && ./install.sh
230
+ ```
231
+
232
+ Select theme: **Cmd/Ctrl+Shift+P** → "Color Theme" → "Struckdown Dark"
233
+
234
+ ## Basic Syntax
235
+
236
+ ### Completions (Slots)
237
+
238
+ Use `[[slot]]` to mark where the LLM should respond:
239
+
240
+ ```bash
241
+ sd chat "Explain quantum physics: [[explanation]]"
242
+ ```
243
+
244
+ ### Typed Completions
245
+
246
+ Specify the type of response:
247
+
248
+ ```bash
249
+ # Boolean
250
+ sd chat "Is the sky blue? [[bool:answer]]"
251
+
252
+ # Pick from options
253
+ sd chat "Choose [[pick:color|red,green,blue]]"
254
+
255
+ # Numbers
256
+ sd chat "Price: $19.99 [[number:price]]"
257
+
258
+ # Dates
259
+ sd chat "Meeting on Jan 15, 2024 [[date:meeting]]"
260
+
261
+ # JSON (any valid JSON value)
262
+ sd chat "Return data as JSON [[json:data]]"
263
+
264
+ # Record (JSON object with string keys)
265
+ sd chat "Extract as key-value pairs [[record:info]]"
266
+ ```
267
+
268
+ ### Variables
269
+
270
+ Reference previous extractions with `{{variable}}`:
271
+
272
+ ```
273
+ Extract name: [[name]]
274
+
275
+ <checkpoint>
276
+
277
+ Hello {{name}}, how are you? [[response]]
278
+ ```
279
+
280
+ ### Memory Boundaries
281
+
282
+ Use `<checkpoint>` to create memory boundaries and save tokens:
283
+
284
+ ```
285
+ Long expensive context...
286
+
287
+ Summary: [[summary]]
288
+
289
+ <checkpoint>
290
+
291
+ Translate {{summary}} to Spanish: [[translation]]
292
+ ```
293
+
294
+ Everything before `<checkpoint>` is forgotten -- only extracted variables carry forward.
295
+
296
+ ## CLI Commands
297
+
298
+ ### `sd chat` - Interactive Mode
299
+
300
+ ```bash
301
+ sd chat "Tell me a joke: [[joke]]"
302
+ sd chat -p prompt.sd
303
+ echo "Process this" | sd chat
304
+ ```
305
+
306
+ ### `sd batch` - Batch Processing
307
+
308
+ ```bash
309
+ # Basic usage
310
+ sd batch *.txt "Extract [[name]]" -o results.json
311
+
312
+ # With prompt file
313
+ sd batch *.txt -p prompt.sd -o results.csv
314
+
315
+ # Keep input fields
316
+ sd batch *.txt "[[summary]]" -k
317
+
318
+ # Chain operations
319
+ sd batch *.txt "[[purpose]]" | sd batch "Similar: [[product]]" -k
320
+ ```
321
+
322
+ **Output formats** (auto-detected from extension):
323
+ - `.json` -- JSON array
324
+ - `.csv` -- CSV file
325
+ - `.xlsx` -- Excel spreadsheet
326
+ - None -- Pretty-printed to stdout
327
+
328
+ ### `sd check` - Validate Prompts
329
+
330
+ Check prompt syntax and display execution plan:
331
+
332
+ ```bash
333
+ # Validate and show structure
334
+ sd check prompt.sd
335
+ ```
336
+
337
+ Shows system prompt info, sections, completions, dependencies, and line numbers.
338
+
339
+ ### `sd graph` - Visualize Prompts
340
+
341
+ Generate dependency graph visualizations:
342
+
343
+ ```bash
344
+ # Generate HTML visualization (default)
345
+ sd graph prompt.sd
346
+
347
+ # Generate Mermaid diagram text
348
+ sd graph prompt.sd -o diagram.mmd
349
+ ```
350
+
351
+ Creates interactive diagrams showing sections, completions, dependencies, and execution flow.
352
+
353
+ ### `sd flat` - Flatten Templates
354
+
355
+ Resolve all `{% include %}` directives and output flattened template:
356
+
357
+ ```bash
358
+ # Output to stdout
359
+ sd flat prompt.sd
360
+
361
+ # Save to file
362
+ sd flat prompt.sd -o flattened.sd
363
+ ```
364
+
365
+ Useful for debugging includes or creating self-contained templates.
366
+
367
+ ## File Includes
368
+
369
+ Struckdown supports file includes using Jinja2's `{% include %}` syntax:
370
+
371
+ ```struckdown
372
+ {# Include shared system prompt #}
373
+ {% include 'common/system.sd' %}
374
+
375
+ {# Include evaluation rubric #}
376
+ {% include 'rubrics/essay_criteria.txt' %}
377
+
378
+ Process: {{input}}
379
+ Result: [[output]]
380
+ ```
381
+
382
+ **Search paths** (in priority order):
383
+ 1. Same directory as template file
384
+ 2. `templates/` subdirectory relative to template file
385
+ 3. `./includes/` (project-local includes)
386
+ 4. `./templates/` (project-local templates)
387
+ 5. `~/.struckdown/includes/` (global user includes)
388
+
389
+ **Advanced includes:**
390
+
391
+ ```struckdown
392
+ {# Conditional includes #}
393
+ {% if verbose %}
394
+ {% include 'detailed_instructions.sd' %}
395
+ {% else %}
396
+ {% include 'brief_instructions.sd' %}
397
+ {% endif %}
398
+
399
+ {# Dynamic includes with variables #}
400
+ {% set rubric = 'rubrics/' + grade_level + '.sd' %}
401
+ {% include rubric %}
402
+ ```
403
+
404
+ ## Caching
405
+
406
+ Struckdown automatically caches LLM responses to disk:
407
+
408
+ ```bash
409
+ # Default cache location
410
+ ~/.struckdown/cache # 10 GB limit (LRU eviction)
411
+
412
+ # Disable caching
413
+ export STRUCKDOWN_CACHE=0
414
+
415
+ # Custom cache directory
416
+ export STRUCKDOWN_CACHE=/path/to/cache
417
+
418
+ # Custom size limit (MB)
419
+ export STRUCKDOWN_CACHE_SIZE=5120 # 5 GB
420
+ ```
421
+
422
+ ## Embeddings
423
+
424
+ Generate text embeddings using API or local models:
425
+
426
+ ```python
427
+ from struckdown import get_embedding
428
+
429
+ # API embeddings (default)
430
+ embeddings = get_embedding(["text 1", "text 2"])
431
+
432
+ # Local embeddings (requires: uv pip install struckdown[local])
433
+ embeddings = get_embedding(texts, model="local/all-MiniLM-L6-v2")
434
+ ```
435
+
436
+ Use `local/model-name` prefix for any sentence-transformers model. API embeddings use `LLM_API_KEY` and `LLM_API_BASE` environment variables.
437
+
438
+ ## Advanced Features
439
+
440
+ ### List Completions
441
+
442
+ Extract multiple items:
443
+
444
+ ```bash
445
+ # Exactly 3 items
446
+ sd chat "Name 3 fruits: [[3*pick:fruit|apple,banana,orange]]"
447
+
448
+ # Between 2 and 4 items
449
+ sd chat "Name 2-4 animals: [[2:4*extract:animals]]"
450
+
451
+ # Any number
452
+ sd chat "List all mentioned: [[*extract:items]]"
453
+ ```
454
+
455
+ ### Date/Time Extraction
456
+
457
+ ```bash
458
+ # Single date
459
+ sd chat "Meeting Jan 15 [[date:when]]"
460
+
461
+ # Date range with pattern expansion
462
+ sd chat "Every Tuesday in October [[date*:dates]]"
463
+
464
+ # With validation
465
+ sd chat "Deadline [[!date:deadline]]" # ! makes it required
466
+ ```
467
+
468
+ ### Number Extraction
469
+
470
+ ```bash
471
+ # With constraints
472
+ sd chat "Age (0-120): [[number:age|min=0,max=120]]"
473
+
474
+ # Required numbers
475
+ sd chat "Price: [[!number:price]]"
476
+
477
+ # Multiple numbers
478
+ sd chat "Extract all prices: [[number*:prices]]"
479
+ ```
480
+
481
+ ### Custom Actions
482
+
483
+ Extend Struckdown with Python functions:
484
+
485
+ ```python
486
+ from struckdown import Actions, chatter
487
+
488
+ @Actions.register('uppercase')
489
+ def uppercase_text(context, text: str):
490
+ return text.upper()
491
+
492
+ # Use in template - unquoted 'input' is a variable reference
493
+ result = chatter("[[@uppercase:loud|text=input]]", context={"input": "hello"})
494
+ ```
495
+
496
+ See **[Custom Actions Guide](docs/CUSTOM_ACTIONS.md)** for details.
497
+
498
+ ### System Messages
499
+
500
+ Control system messages using XML-style `<system>` tags:
501
+
502
+ ```
503
+ <system>You are an expert data analyst with 10 years of experience.</system>
504
+
505
+ <system local>Always provide concise, data-driven responses.</system>
506
+
507
+ First analysis: [[analysis1]]
508
+
509
+ <checkpoint>
510
+
511
+ Second analysis: [[analysis2]]
512
+ ```
513
+
514
+ **Global system messages** (`<system>`) set the LLM's role and persist across all checkpoints.
515
+ **Local system messages** (`<system local>`) provide instructions that only apply to the current segment.
516
+
517
+ Multiple `<system>` tags append to the system message by default. Use modifiers:
518
+
519
+ ```
520
+ <system>Base instructions.</system>
521
+ <system>Additional instructions.</system> <!-- appends -->
522
+ <system replace>Replace all previous.</system> <!-- replaces -->
523
+
524
+ <system local>This segment only.</system> <!-- cleared after checkpoint -->
525
+ <system local replace>New local only.</system> <!-- replaces local -->
526
+ ```
527
+
528
+ All support template variables: `{{variable}}`.
529
+
530
+ ### Model/Temperature Overrides
531
+
532
+ Override per-slot settings:
533
+
534
+ ```
535
+ # Different temperature
536
+ [[think:reasoning|temperature=0.3]]
537
+
538
+ # Different model
539
+ [[pick:choice|red,blue|model=gpt-4]]
540
+
541
+ # Combine
542
+ [[extract:data|model=gpt-4,temperature=0.0]]
543
+ ```
544
+
545
+ ## Examples
546
+
547
+ See **[examples/](examples/)** for:
548
+ - Basic completions
549
+ - Multi-step workflows
550
+ - RAG with custom actions
551
+ - Batch processing patterns
552
+ - Date/time extraction
553
+ - Number validation
554
+
555
+ ## Contributing
556
+
557
+ Issues and pull requests welcome at [github.com/benwhalley/struckdown](https://github.com/benwhalley/struckdown)
558
+
559
+ ## License
560
+
561
+ MIT