Skip to content

Build an Agentic Coding Loop with GLM

Wire an AI SDK tool-calling loop to a GLM coding model through AnyRouter, with streaming, multi-step tool use, and an optional BYOK Z-AI key.

A coding agent is a model that can call tools — read a file, run a test, search a codebase — and keep going until the task is done. AnyRouter gives you one provider for the model, and the Vercel AI SDK handles the tool-calling loop. GLM models are a strong, low-cost fit: long context and controllable reasoning.

The idea

The AI SDK runs the loop for you. When the model emits a tool call, the SDK executes the matching tool, feeds the result back, and asks the model what to do next — repeating until the task is done or a step budget is hit. AnyRouter is just the model provider, so the same loop works with any tool-capable model by changing the model id. This example uses z-ai/glm-4.7-flash.

flowchart LR
  agent["AI SDK loop"] -->|prompt| model["anyrouter(z-ai/glm-4.7-flash)"]
  model -->|tool call| tools["read_file · list_files · run_test"]
  tools -->|result| model
  model -->|final answer| done["Done (stopWhen)"]

How it works

stopWhen: stepCountIs(n) bounds how many steps the loop may take. Each entry in the returned steps array records the model's tool calls and their results, so you can log or inspect exactly what the agent did.

Implementation

Install the packages

npm install @anyr/ai-sdk-provider ai zod

Define tools and run the loop

import { createAnyRouter } from "@anyr/ai-sdk-provider"
import { generateText, tool, stepCountIs } from "ai"
import { z } from "zod"
import { readFile } from "node:fs/promises"

const anyrouter = createAnyRouter() // reads ANYROUTER_API_KEY

const { text, steps } = await generateText({
  model: anyrouter("z-ai/glm-4.7-flash"),
  system: "You are a coding agent. Use the tools to inspect the repo, then answer.",
  prompt: "What does src/index.ts export, and is it covered by a test?",
  stopWhen: stepCountIs(8),
  tools: {
    read_file: tool({
      description: "Read a UTF-8 text file from the repo.",
      parameters: z.object({ path: z.string() }),
      execute: async ({ path }) => ({ contents: await readFile(path, "utf8") }),
    }),
    list_files: tool({
      description: "List files matching a glob.",
      parameters: z.object({ glob: z.string() }),
      // findFiles is your own glob helper (e.g. fast-glob's `glob(pattern)`).
      execute: async ({ glob }) => ({ files: await findFiles(glob) }),
    }),
  },
})

console.log(text)
console.log(`completed in ${steps.length} steps`)

Stream the agent's progress (optional)

For a responsive UI or CLI, swap generateText for streamText and consume the full stream — text deltas, tool calls, and tool results all arrive as they happen:

import { streamText, stepCountIs } from "ai"

const result = streamText({
  model: anyrouter("z-ai/glm-4.7-flash"),
  prompt: "Refactor the failing test and explain the change.",
  stopWhen: stepCountIs(10),
  tools: { /* read_file, list_files, run_test, ... */ },
})

for await (const part of result.fullStream) {
  if (part.type === "text-delta") process.stdout.write(part.text)
  if (part.type === "tool-call") console.log("\n→ calling", part.toolName)
}

Tune reasoning with effort (optional)

GLM supports controllable thinking. Pass reasoning_effort through the provider's extraBody, which is merged into the request body sent to AnyRouter:

const { text } = await generateText({
  model: anyrouter("z-ai/glm-4.7-flash"),
  prompt: "Plan a migration from REST to tRPC across this repo.",
  providerOptions: { anyrouter: { extraBody: { reasoning_effort: "high" } } },
  stopWhen: stepCountIs(12),
  tools: { /* ... */ },
})

Bring your own GLM key. If you have a Z-AI account or Coding Plan, add the key in Settings → BYOK. AnyRouter then serves z-ai/* requests with your key — same agent code, billed to your Z-AI account at list price.