Skip to content

Inline Python Expressions

Inline Python is a single expression enclosed in one pair of backticks. It is evaluated in the active Kedi runtime environment.

Expression Syntax

[radius: float] = `3.5`
[area: float] = `3.14159 * radius ** 2`
= area\=<`round(area, 2)`>

The content must be a Python expression. Assignments, import, for, try, and statement return require a fenced block. Comprehensions, conditional expressions, lambdas, and function calls are expressions and are allowed.

Expressions in Templates

Wrap an expression in angle brackets or use a bare backtick segment:

[services: list[str]] = `["api", "worker"]`
>> Assess <`len(services)`> services: <`", ".join(services)`>.
Return [risk: str].

Both Python forms insert the expression's string representation into rendered text. Prefer <`...`> when visually separating Kedi substitutions; a bare segment can be clearer when the Python contains <, >, or brackets.

Expressions in Assignments

A sole expression preserves its native value:

[threshold: float] = `0.8`
[labels: list[str]] = `["safe", "review"]`

Literal text around the expression changes the assignment to rendered str:

[message] = threshold\=<`threshold`>

This rule prevents accidental conversion in typed pipelines. Use a native assignment for data and a rendered assignment for presentation.

Expressions in Returns

A sole expression can satisfy a native return annotation:

@enabled(flags: dict[str, bool], name: str) -> bool:
  = `flags.get(name, False)`

Inside a rendered return, it contributes text:

@summary(count: int) -> str:
  = Processed <`count`> <`"item" if count == 1 else "items"`>.

An unannotated procedure has a str return contract. Annotate it before returning a native bool, number, collection, or custom model.

Expressions in Call Arguments

A backtick call argument passes the native result:

@limit(values: list[int], count: int) -> list[int]:
  = `values[:count]`

= `limit([1, 2, 3, 4], count=2)`

Angle calls render their result as text. Use them for string composition, not for preserving a native list:

@format_limit(values: list[int], count: int) -> str:
  = `", ".join(str(value) for value in values[:count])`

= First values: <format_limit(`[1, 2, 3, 4]`, `2`)>

Backticks on the arguments preserve the native list and integer passed into format_limit; the surrounding angle call deliberately renders its str return. Use the Python call form for keyword arguments.

Expressions in Types and Defaults

Runtime type expressions and defaults use backticks:

```
from typing import Literal
Mode = Literal["fast", "safe"]
```

@run(mode: `Mode` = `"safe"`) -> `Mode`:
  = `mode`

Defaults are evaluated at declaration time. Type expressions resolve when their contract is built or used. Prefer ordinary direct annotations when a type is already known to Kedi.

Expressions in Directives

Directives accept Python expressions where their reference page explicitly allows dynamic values:

```
selected_model = "groq:qwen/qwen3-32b"
```

> model: `selected_model`
> settings:
    temperature: `0.1`
    stop: `["END"]`

Plain settings already parse booleans, numbers, and null; use Python only for lists, mappings, computed values, or objects.

Available Names

Expressions can read visible Kedi variables, procedure parameters, outer lexical values, imports, prelude helpers, compiled Kedi procedures, custom types, and the runtime type namespace.

```
from pathlib import Path

def basename(value: str) -> str:
    return Path(value).name
```

@show(path: str) -> str:
  = `basename(path)`

Names are resolved according to source and lexical scope. A declaration that appears later is not available retroactively.

Side Effects

Python technically allows side effects inside calls and assignment expressions, but value expressions should remain referentially clear. Use a single-backtick statement line or fenced block for intentional mutation:

@increment() -> int:
  [count: int] = `0`
  `count += 1`
  = `count`

This makes execution order visible and keeps prompts, evals, and traces easier to interpret.

Error Mapping

Syntax errors, missing names, assertion failures, and exceptions are reported with their originating Kedi source span. Kedi does not replace failed expressions with empty text. Catch an exception only when the program has a meaningful recovery policy; otherwise let the runtime preserve the failure.