FluxyChat

Guides

Unified Reasoning

> Cross-provider reasoning control for AI generation. Configure how much "thinking" a model does before answering, with a portable API that works across OpenAI,

Cross-provider reasoning control for AI generation. Configure how much "thinking" a model does before answering, with a portable API that works across OpenAI, Anthropic, Google, and more.

Overview

FluxyChat provides a unified reasoning parameter that abstracts away provider-specific reasoning APIs. Instead of learning OpenAI's reasoning_effort, Anthropic's thinking.budget_tokens, or Google's thinkingConfig, you use one config object that maps to each provider automatically.

Quick Start

import { generate, stream } from "@fluxy-chat/agent";

// Generate with high reasoning effort
const result = await generate({
  model: myModel,
  prompt: "What is the square root of 144?",
  reasoning: { effort: "high", summary: "detailed" },
});

console.log(result.text);           // "12"
console.log(result.reasoningText);   // "To find the square root of 144, I need..."
console.log(result.usage.reasoningTokens);  // 832

Configuration

The AIReasoningConfig interface:

interface AIReasoningConfig {
  effort?: "low" | "medium" | "high";        // How hard the model thinks
  summary?: "auto" | "concise" | "detailed" | "none";  // Reasoning summary style
  budgetTokens?: number;                     // Max tokens for reasoning
}

Effort Levels

LevelOpenAIAnthropicGoogle
lowreasoning_effort: "low"budget_tokens: 2000thinkingBudget: 1024
mediumreasoning_effort: "medium"budget_tokens: 8000thinkingBudget: 8192
highreasoning_effort: "high"budget_tokens: 32000thinkingBudget: 24576

Boolean Shorthand

Pass true as a shorthand for \{ effort: "medium", summary: "auto" \}:

await generate({
  model,
  prompt: "Explain quantum entanglement",
  reasoning: true,  // equivalent to { effort: "medium", summary: "auto" }
});

Streaming with Reasoning

Reasoning parts are emitted as stream events before the text response:

import { stream } from "@fluxy-chat/agent";

const result = stream({
  model: myModel,
  prompt: "Design a REST API for a todo app",
  reasoning: { effort: "high" },
});

for await (const part of result.stream) {
  switch (part.type) {
    case "reasoning-start":
      console.log("Model is thinking...");
      break;
    case "reasoning-delta":
      process.stdout.write(part.delta);
      break;
    case "reasoning-end":
      console.log("\n--- Reasoning complete ---");
      break;
    case "text-delta":
      process.stdout.write(part.delta);
      break;
    case "finish":
      console.log("\nUsage:", part.usage);
      break;
  }
}

const resolved = await result.result;
console.log("Reasoning:", resolved.reasoningText);

Provider Mapping

Use reasoningToProviderOptions to convert the unified config to provider-specific options:

import { reasoningToProviderOptions } from "@fluxy-chat/agent";

// For OpenAI
reasoningToProviderOptions({ effort: "high" }, "openai");
// → { reasoning_effort: "high" }

// For Anthropic
reasoningToProviderOptions({ effort: "high" }, "anthropic");
// → { thinking: { type: "enabled", budget_tokens: 32000 } }

// For Google
reasoningToProviderOptions({ effort: "low" }, "google");
// → { thinkingConfig: { thinkingBudget: 1024 } }

Extracting Reasoning

import { extractReasoningText } from "@fluxy-chat/agent";

const reasoning = extractReasoningText(streamParts);
// → "Let me think about this..."

Usage Tracking

Reasoning tokens are included in the AIUsage object:

const result = await generate({
  model,
  prompt: "Complex question",
  reasoning: { effort: "high" },
});

console.log(result.usage);
// {
//   inputTokens: 12,
//   outputTokens: 45,
//   reasoningTokens: 832,  // ← reasoning token count
//   totalTokens: 889
// }

Testing with Reasoning

Use DeterministicLanguageModel with a reasoning response for tests:

import { DeterministicLanguageModel } from "@fluxy-chat/agent";

const model = new DeterministicLanguageModel(
  "The answer is 42.",                                    // text response
  "I considered multiple approaches...",                  // reasoning response
  "test-model",
);

const result = await generate({
  model,
  prompt: "What is the answer?",
  reasoning: { effort: "high" },
});

expect(result.reasoningText).toBe("I considered multiple approaches...");
expect(result.usage.reasoningTokens).toBe(31);

API Reference

ExportTypeDescription
AIReasoningConfiginterfaceConfiguration object for reasoning
AIReasoningEfforttype"low" | "medium" | "high"
AIReasoningSummarytype"auto" | "concise" | "detailed" | "none"
AIReasoningResultinterfaceExtracted reasoning result
normalizeReasoningConfigfunctionNormalizes config (handles true shorthand)
isReasoningConfigfunctionType guard for AIReasoningConfig
reasoningToProviderOptionsfunctionMaps config to provider-specific options
reasoningToOpenAIOptionsfunctionMaps to OpenAI format
reasoningToAnthropicOptionsfunctionMaps to Anthropic format
reasoningToGoogleOptionsfunctionMaps to Google format
extractReasoningTextfunctionExtracts reasoning text from stream parts
mergeReasoningProviderMetadatafunctionMerges reasoning config into provider metadata

On this page