nimgent

Lightweight LLM client library for Nim

Active Pure Nim score 65/100 · last commit 2026-09-05 · 0 stars · tests present · no docs generated

Summary

Latest Version 0.1.0
License MIT
CI Status Failing
Stars 0
Forks 0
Open Issues 0
Last Commit 2026-09-05
Downloads 0
Last Indexed 2026-09-06 07:35

Authors

  • martin

Installation

nimble install nimgent
choosenim install nimgent
git clone https://github.com/martineastwood/nimgent

OS Compatibility

Platform Linux macOS Windows FreeBSD OpenBSD NetBSD Android iOS WASM Embedded
nimgent - - - - - - -

Dependencies

Package Version Optional
nim >= 2.0.0 No

README

nimgent

A lightweight LLM client library for Nim — generate and stream text across providers without pulling in an agent loop, TUI, or coding tools.

Install

nimble install nimgent

Or path-depend during development (sibling checkout):

# nim.cfg
--path:"../nimgent/src"

Quick start

import std/os
import nimgent
import nimgent/openrouter

let provider = makeOpenRouterProvider(
  getEnv("OPENROUTER_API_KEY"),
  "https://openrouter.ai/api/v1/chat/completions")

let response = generateText(
  provider,
  model = "deepseek/deepseek-v4-flash-0731",
  prompt = "Say hello in one sentence.")

echo response.textContent

Stream tokens as they arrive:

let response = streamText(
  provider,
  model = "deepseek/deepseek-v4-flash-0731",
  prompt = "Count to three.",
  onEvent = proc (ev: StreamEvent): bool =
    if ev.kind == seTextDelta:
      stdout.write ev.text
      flushFile(stdout)
    true)

echo ""
echo "finish: ", response.finishReason

Providers

import nimgent/[anthropic, hyper, openai, openrouter]

let openai = makeOpenAIProvider(getEnv("OPENAI_API_KEY"))
# /v1/responses; pass a */chat/completions URL for compat servers

let hyper = makeHyperProvider(getEnv("HYPER_API_KEY"))
# https://hyper.charm.land/v1/chat/completions

let anthropic = makeAnthropicProvider(
  getEnv("ANTHROPIC_API_KEY"),
  "https://api.anthropic.com/v1/messages")

Provider-specific knobs (thinking, routing, cache TTL) go in ProviderRequest.options / the options argument on generateText / streamText.

Retries, cancel, tools

generateText / streamText retry 429, 5xx, and transport errors (maxRetries defaults to 2) with jittered backoff and Retry-After. Context overflow and 4xx are not retried.

Pass abort to cancel before the next attempt (or, while streaming, from onEvent by returning false):

var stop = false
let response = generateText(
  provider, model = "…", prompt = "…",
  abort = proc (): bool = stop)

Tools with an execute callback plus maxSteps > 1 run a small loop: model → tools → model, until there are no tool calls or the step cap is hit. maxSteps defaults to 1 (one model call, no loop).

let weather = tool("weather", "Look up weather",
  %*{"type": "object", "properties": {"city": {"type": "string"}}},
  proc (input: JsonNode): ToolOutput =
    ToolOutput(output: "16C and cloudy"))

let response = generateText(
  provider,
  model = "…",
  prompt = "Weather in Paris?",
  tools = @[weather],
  maxSteps = 5)

niminal still owns its own agent loop; this helper is for apps that want the AI-SDK-style “run my callbacks until the model is done.”

Structured output

generateObject asks the model for JSON that matches a schema and validates it. Truncated JSON is closed locally (fixJson). OpenAI (Responses and Chat Completions), OpenRouter, Hyper, and Anthropic get native structured-output knobs automatically; anything else is prompt + extract (including ``` fences).

type Recipe = object
  name: string
  servings: int
  ingredients: seq[string]

let recipe = generateObject[Recipe](
  provider,
  model = "…",
  prompt = "A weeknight lasagna.")

echo recipe.value.name
echo recipe.usage.inputTokens

Pass schema = %*{...} for the JsonNode form. mode = omTool forces a submit tool. maxRepairs (default 0) is optional extra model turns after local repair fails.

streamObject streams the first attempt. onPartial receives the JSON tree whenever it changes (unclosed strings/objects are closed; not schema-valid until the stream ends). Schema check and model repairs run after that.

let recipe = streamObject[Recipe](
  provider,
  model = "…",
  prompt = "A weeknight lasagna.",
  onPartial = proc (partial: JsonNode): bool =
    if "name" in partial:
      stdout.write "\r" & partial["name"].getStr
      flushFile(stdout)
    true)

What this is not

nimgent is not a coding agent. Session persistence, compaction, workspace tools, and terminal UI live in niminal (or your own app).

Release

git tag -a v0.1.0 -m "nimgent 0.1.0"
nimble publish   # needs a GitHub PAT with public_repo scope