Complete#
complete(tool_name, prompt) generates structured data via an LLM using OpenAI-compatible chat completion with tool calling. The LLM is forced to return data matching a tool schema defined in the YAML config. Access individual fields with dot notation.
Use locals to call once and access multiple fields:
locals:
review: 'complete("review", "Review: " + name)'
args:
- local("review").review_text
- local("review").sentiment
- local("review").ratingLocals are re-evaluated per row in batch mode. Query-level locals override transaction locals when both exist.
Configuration#
| Flag | Env Var | Default |
|---|---|---|
--complete-api-key | EDG_COMPLETE_API_KEY | (required) |
--complete-url | EDG_COMPLETE_URL | https://api.openai.com/v1/chat/completions |
--complete-model | EDG_COMPLETE_MODEL | gpt-4o |
Any OpenAI-compatible API works (Ollama, vLLM, Azure OpenAI, etc.) - set --complete-url to point at your endpoint.
Tool Schema#
Tools are defined in the complete YAML section. Each tool has a name, system prompt, and JSON Schema properties:
complete:
tools:
- name: review
system: |-
You are a product review generator. Generate realistic reviews
for consumer electronics. IMPORTANT: vary ratings across the full
1-5 range. Most products should get 2-4 stars. 5-star and 1-star
reviews should be rare. Match sentiment and review_text to the
rating you choose.
properties:
review_text:
type: string
description: "A 2-3 sentence product review from a customer"
sentiment:
type: string
enum: [positive, negative, neutral]
description: "Overall sentiment of the review"
rating:
type: integer
description: "Star rating from 1 to 5"
required: [review_text, sentiment, rating]You can define multiple tools in the same config. Each tool name is referenced in the complete() call.
Execution Modes#
Immediate mode (exec/query)#
For single-row queries, complete() calls the API directly. Memoization ensures that multiple field accesses with the same tool and prompt within a row make only one API call:
run:
- name: insert_review
type: exec
locals:
review: 'complete("review", "Review: " + ref_rand("product_catalog").name)'
args:
- ref_rand("product_catalog").name
- local("review").review_text
- local("review").sentiment
- local("review").rating
query: |-
INSERT INTO review (product_name, review_text, sentiment, rating)
VALUES ($1, $2, $3, $4)Deferred mode (exec_batch/query_batch)#
For batch queries, all complete() calls are collected as placeholders during row evaluation, then resolved concurrently (up to 8 parallel requests) after all rows are generated. Placeholders are replaced in the formatted SQL before execution.
seed:
- name: populate_review
type: exec_batch
count: 20
size: 5
locals:
review: 'complete("review", "Review: " + ref_each(product_catalog).name)'
args:
- ref_each(product_catalog).name
- local("review").review_text
- local("review").sentiment
- local("review").rating
query: |-
INSERT INTO review (product_name, review_text, sentiment, rating)
__values__Array mode (complete_array)#
complete_array(tool_name, prompt, count) generates N items in a single API call. The tool schema is automatically wrapped in an items array request. Use ref_each() to iterate through the results:
seed:
- name: populate_review
type: exec_batch
count: products
size: products
locals:
reviews: 'complete_array("review", "Generate " + string(products) + " product reviews", products)'
args:
- ref_each(product_catalog).name
- ref_each(local("reviews")).review_text
- ref_each(local("reviews")).sentiment
- ref_each(local("reviews")).rating
query: |-
INSERT INTO review (product_name, review_text, sentiment, rating)
__values__This is more efficient than complete() in a loop because all N items are generated in 1 API call. The result is memoized by (tool_name, prompt, count) so multiple field accesses within a row make only one call.
A full working example is available at _examples/complete_array/.
Example#
A full working example is available at _examples/complete/.
globals:
products: 20
batch_size: 5
reference:
product_catalog: !include products.yaml
complete:
tools:
- name: review
system: |-
You are a product review generator. Generate realistic reviews
for consumer electronics. IMPORTANT: vary ratings across the full
1-5 range. Most products should get 2-4 stars. 5-star and 1-star
reviews should be rare. Match sentiment and review_text to the
rating you choose.
properties:
review_text:
type: string
description: "A 2-3 sentence product review"
sentiment:
type: string
enum: [positive, negative, neutral]
rating:
type: integer
description: "Star rating from 1 to 5"
required: [review_text, sentiment, rating]
seed:
- name: populate_review
type: exec_batch
count: products
size: batch_size
locals:
review: 'complete("review", "Review: " + ref_each(product_catalog).name + " - " + ref_each(product_catalog).description)'
args:
- ref_each(product_catalog).name
- local("review").review_text
- local("review").sentiment
- local("review").rating
query: |-
INSERT INTO review (product_name, review_text, sentiment, rating)
__values__
run:
- name: insert_review
type: exec
locals:
review: 'complete("review", "Review: " + ref_rand("product_catalog").name)'
args:
- ref_rand("product_catalog").name
- local("review").review_text
- local("review").sentiment
- local("review").rating
query: |-
INSERT INTO review (product_name, review_text, sentiment, rating)
VALUES ($1, $2, $3, $4)Reliability#
Each LLM request retries up to 3 times with backoff (500ms, 1s) when:
- The model returns no tool call
- The model echoes the schema definition instead of generating values (e.g. returning
{"type": "integer", "description": "..."}where an integer was expected)
Responses are validated against the tool schema. Property types (string, integer, number, boolean) must match. Invalid responses trigger a retry.
The per-request HTTP timeout is 120 seconds, which accommodates slower local models.
Using with Ollama#
edg seed \
--complete-api-key ollama \
--complete-url http://localhost:11434/v1/chat/completions \
--complete-model qwen3:8b \
...Tool calling support varies by model. Use models with reliable function calling support.
qwen3:8b(5.2 GB) is a good default if you can prompt around the inherent sycophantism of smaller models. Larger models likeqwen3:32bare more reliable but heavier. The built-in retry logic handles intermittent failures from smaller models.