Parameters and Returns¶
Procedure signatures define the native contract between Kedi, Python, tools,
tests, and evals. An omitted annotation is not Any: it defaults to str.
Positional and Typed Parameters¶
@render_issue(id: int, title: str, tags: list[str]) -> str:
= \#<`id`> <title> \[<`", ".join(tags)`>\]
Kedi validates each argument against its annotation. It does not coerce a
rendered "42" into 42, and it does not accept a dict where a custom model is
required.
Direct type names and runtime Python type expressions are equivalent:
@first(items: `list[str]`) -> `str`:
= `items[0]`
Prefer direct annotations when possible because the LSP can resolve and explain them statically. Use backtick annotations for a type supplied by Python state.
Default Parameters¶
Defaults are single-line Python expressions:
@format_count(count: int, label: str = `"items"`, compact: bool = `False`) -> str:
= `<f"{count}{label}" if compact else f"{count} {label}">`
= <format_count(`3`)>
Required parameters must precede defaulted parameters. Duplicate parameters and fenced block defaults are parse errors.
Defaults are evaluated when the procedure is defined, not on each call:
```
seed = 1
```
@value(current: int = `seed`) -> int:
= `current`
`seed = 9`
= `value()` # 1
Untyped defaults retain their native Python value, but the parameter itself still follows the signature contract. Annotate non-string defaults explicitly for clarity.
Call Arguments¶
Kedi angle-call syntax is positional:
= <format_count(`3`, records, `True`)>
Use single backticks to pass a native Python result. Plain text and mixed
template arguments render to str. Escape text commas as \,.
Named arguments are available through Python because compiled Kedi procedures have real Python signatures:
= `format_count(count=3, compact=True, label="records")`
Use angle calls for normal Kedi composition. Use Python keyword calls when argument order would be unclear or values are already native.
Declared Return Types¶
Return annotations are enforced:
@active_ids(rows: list[dict[str, object]]) -> list[int]:
= `[row["id"] for row in rows if row.get("active")]`
Without -> ..., a procedure has a str return contract. Returning a native
integer, list, or model from an untyped procedure is an error. Annotate every
procedure that intentionally returns a non-string.
Kedi validates rather than coerces return values. = "5" cannot satisfy
-> int; use = `5` or parse explicitly.
Rendered and Native Returns¶
A rendered return starts with = and combines literal text, substitutions,
calls, and inline Python:
@status(name: str, healthy: bool) -> str:
= <name>: <`"healthy" if healthy else "degraded"`>
A sole backtick expression returns its native result:
@total(values: list[int]) -> int:
= `sum(values)`
A fenced block can return a value after several statements:
@median(values: list[float]) -> float:
= ```
ordered = sorted(values)
middle = len(ordered) // 2
if len(ordered) % 2:
return ordered[middle]
return (ordered[middle - 1] + ordered[middle]) / 2
```
Choose a rendered return for human-facing text and a native return for values that callers will calculate with, validate, serialize, or pass to tools.
Multiline Returns¶
Use a trailing backslash to continue one rendered return across physical lines:
@notice(name: str) -> str:
= Hello <name>, \
your report is ready.
Return continuation is a text feature. It is not how multiline model prompts
work; >> prompts use same-indentation continuation lines instead. Use \\ for
a literal backslash.
Kedi trims line-end whitespace while preserving meaningful internal spacing.
Escaped whitespace (\s, \t, and \n) remains significant even at a
boundary.
Early Return and No Return¶
Execution ends at the first reached return. Statements after an unconditional return are unreachable and should be removed.
An untyped procedure with no returned value produces "". Relying on that is
appropriate only for side-effect-oriented helpers. Public procedures should
return deliberately and declare the contract.
Errors¶
Kedi reports missing and extra arguments, invalid default ordering, unresolved types, parameter mismatches, and return mismatches. These errors occur at the boundary where the contract is violated, before an invalid value silently propagates into later model or tool calls.