Nous.CodeMode.Sdk (nous v0.17.1)

Copy Markdown View Source

Renders the typed SDK a Code Mode program is written against.

Under mode: :code this text is the model's only declaration of the tools — there is no native tool schema alongside it — which is what drives every rule below.

Byte stability

Tools are sorted lexicographically by name and every map is walked in a sorted order, so the same tool set renders to the same bytes on every call. The SDK travels in the run_code tool's description, i.e. in the prompt prefix of every request; an unstable ordering would invalidate the provider's KV cache each turn and silently double the cost of a run. Assert render(lang, tools) == render(lang, Enum.shuffle(tools)) when you change anything here.

Total, never raising, on schemas

json_schema_to_type/2 is total over arbitrary input. Any node it cannot express — a $ref it cannot resolve, an unknown type, a malformed map, a nesting deeper than a dozen levels — becomes the language's escape hatch (unknown for JS/TS, Any for Python). An exotic schema costs that node its type; it must never cost the SDK its parseability, because a program written against an unparseable SDK cannot call any tool at all.

Addressing

tools is an object/mapping indexed by each tool's exact name, so tools["my-tool"](args) works with no alias table and no name mangling. Names are emitted as JSON string literals, which are valid string literals in both target languages.

Languages

:javascript and :typescript render the same TypeScript declaration — under JavaScript it is read as documentation, not executed. :python renders a stub module. Structural fidelity differs by language and that is deliberate: Python has no intersection type and no anonymous nested record type, so allOf and nested objects degrade there while JS/TS keeps them.

Summary

Types

A language this generator can render. Providers report their language as a string via Nous.CodeRuntime.language/1; both forms are accepted.

Functions

Resolve a language given as an atom or a string.

The exception class name the SDK tells the program to expect from a failed call. Bindings carry the same name in Nous.CodeRuntime.Binding.error_class.

The type a node gets when the generator cannot express it.

The name of the global the program calls tools through.

Convert one JSON Schema node to a language type expression.

Every language render/2 can generate.

Render the SDK declaring tools.

Types

language()

@type language() :: :javascript | :typescript | :python

A language this generator can render. Providers report their language as a string via Nous.CodeRuntime.language/1; both forms are accepted.

Functions

coerce_language(language)

@spec coerce_language(term()) :: {:ok, language()} | :error

Resolve a language given as an atom or a string.

Returns :error rather than raising, so a caller holding a language it read from provider configuration can fall back instead of failing a run.

Examples

iex> Nous.CodeMode.Sdk.coerce_language("typescript")
{:ok, :typescript}

iex> Nous.CodeMode.Sdk.coerce_language(:ruby)
:error

error_class()

@spec error_class() :: String.t()

The exception class name the SDK tells the program to expect from a failed call. Bindings carry the same name in Nous.CodeRuntime.Binding.error_class.

escape_hatch(language)

@spec escape_hatch(language() | String.t()) :: String.t()

The type a node gets when the generator cannot express it.

Examples

iex> Nous.CodeMode.Sdk.escape_hatch(:typescript)
"unknown"

iex> Nous.CodeMode.Sdk.escape_hatch(:python)
"Any"

global()

@spec global() :: String.t()

The name of the global the program calls tools through.

The bindings a provider is handed must use this same global, or the SDK describes something the program cannot reach.

json_schema_to_type(language, schema)

@spec json_schema_to_type(language() | String.t(), term()) :: String.t()

Convert one JSON Schema node to a language type expression.

Total: any node the generator cannot express returns escape_hatch/1 rather than raising. Accepts string- or atom-keyed schema maps.

Examples

iex> Nous.CodeMode.Sdk.json_schema_to_type(:typescript, %{"type" => "integer"})
"number"

iex> Nous.CodeMode.Sdk.json_schema_to_type(:python, %{"type" => "array"})
"list[Any]"

iex> Nous.CodeMode.Sdk.json_schema_to_type(:typescript, %{"$ref" => "#/definitions/x"})
"unknown"

languages()

@spec languages() :: [language()]

Every language render/2 can generate.

render(language, tools)

@spec render(language() | String.t(), [Nous.Tool.t()]) :: String.t()

Render the SDK declaring tools.

Tools are deduplicated by name (first occurrence wins, matching the runner's own lookup) and sorted lexicographically, so output depends on the tool set, never on the order it arrived in.

Raises ArgumentError for a language this generator does not know; that is provider configuration, not model input.

Examples

iex> tool = %Nous.Tool{
...>   name: "greet",
...>   description: "Say hi",
...>   function: fn _ctx, _args -> {:ok, "hi"} end,
...>   parameters: %{
...>     "type" => "object",
...>     "properties" => %{"name" => %{"type" => "string"}},
...>     "required" => ["name"]
...>   }
...> }
iex> sdk = Nous.CodeMode.Sdk.render("javascript", [tool])
iex> sdk =~ ~s|"greet"(args: {|
true