Skip to content

LLM traffic

Applications that use large language models talk to their APIs over HTTPS like any other client. Quena recognises these calls and shows them as what they are: a request to a model, its tools, the answer and what it cost, also when the answer came as a stream. How the calls of one agent run fit together is on Agent conversations.

What is recognised

API Recognised by
OpenAI Chat Completions and every OpenAI-compatible API (Azure OpenAI, Mistral, Groq, OpenRouter, DeepSeek, xAI, Together, Fireworks, LM Studio, vLLM, LiteLLM …) POST …/chat/completions
OpenAI Responses POST …/responses, and over a WebSocket to …/responses (Codex): see below
Anthropic Messages POST …/v1/messages
Google Gemini and Vertex AI POST …:generateContent, …:streamGenerateContent
Ollama POST …/api/chat, …/api/generate
Embeddings POST …/embeddings, …/api/embed, …/api/embeddings, …:embedContent, …:batchEmbedContents
Claude on Google Vertex AI POST …/publishers/anthropic/models/…:rawPredict, …:streamRawPredict (Anthropic's format)
Claude on Amazon Bedrock POST bedrock-runtime…/model/…anthropic.…/invoke, …/invoke-with-response-stream (Anthropic's format, streamed in AWS's event stream)
Amazon Bedrock Converse (any model) POST bedrock-runtime…/model/…/converse, …/converse-stream

Over a WebSocket. Codex sends its calls over a WebSocket to …/responses (OpenAI's Responses API with responses_websockets): each response.create it sends, with the events the server answers up to response.completed, becomes a session of its own next to the WebSocket, a POST with the request as body and the events as a stream (comment Over WebSocket #12 (call 3), flag x-quena-ws-call). They are made while the WebSocket is open, so the LLM view, conversations and costs work as for calls over HTTP. Messages compressed with permessage-deflate are read inflated. An opened archive that holds only the WebSocket (a capture of quena-cli reverse, for example) gets its calls the same way; one saved by the app holds them already.

Threads and stored responses. A call that continues a server-side thread carries only what is new: Claude Code's message threads (thread with previous_message_id) and OpenAI's previous_response_id. Quena links it to the call whose answer it names and puts the whole history together from the calls before it for hints and the context map.

A request counts only when its JSON body carries what that API needs (messages, contents, input …), so another application's /api/chat is not taken for Ollama. HTTPS decryption must be on, as for any HTTPS content.

The provider is named by the API's host (OpenAI, Azure OpenAI, Anthropic, Google Gemini, Google Vertex AI, Amazon Bedrock, Mistral, Groq, OpenRouter …); other hosts, such as a company gateway or a local server, keep their host name (Ollama's APIs are named Ollama). Models named in the URL (Gemini, Vertex AI, Bedrock, e.g. us.anthropic.claude-sonnet-4-5-20250929-v1:0) are taken from there and priced like the provider's own model.

Amazon Bedrock and AWS Signature V4

Bedrock requests are signed with AWS Signature V4 (Authorization: AWS4-HMAC-SHA256 …, or X-Amz-Signature= in a presigned URL). The signature covers the body, so AWS refuses any changed copy with 403. Quena therefore leaves signed requests alone in its LLM rewrite operations, and the prompt playground refuses them. Edits at a breakpoint, generic JSON rewrite rules and replays with a changed body still break the signature. Errors in AWS's event stream (exception frames) and Bedrock's {"message": …} errors show as the call's error.

The LLM view

The LLM view of a Claude Code call: model, tokens and estimated cost, links to its parts, the system prompt and the first message taken apart into titled parts (base prompt, reminder, instruction files, skills), and the answer with its MCP tool call

Selecting such a session opens the LLM view (on both the request and the response side):

  • a header with provider, model, whether the answer was streamed, the stop reason (stop, end_turn, tool_calls, max_tokens …), the token usage (input, output, read from and written to the provider's cache, reasoning) and the estimated cost;
  • the system prompt (also instructions, systemInstruction; Codex sends it in the role developer, shown as System (developer)), without Claude Code's attribution block (x-anthropic-billing-header: …), which changes with every request;
  • the messages sent, by role: text, images as placeholders, tool calls with their arguments and tool results with the call they answer;
  • the answer: text, thinking or reasoning summaries (collapsed), tool calls. Streams (server-sent events, Ollama's JSON lines, Gemini's array, AWS's event stream) are put together;
  • tools and parameters: the tool definitions offered and the parameters sent (temperature, max_tokens, reasoning_effort, thinking …);
  • under the answer, the MCP exchanges that ran its tool calls (see Tool call trail);
  • Context: the call's turn in its conversation (Show conversation opens the Agents panel), the map of what fills its input, the change from the call it continues and the provider's cache verdict, described on Agent conversations.

Prompt parts. Agents put much more into a request than what you typed: their base prompt, skills, sandbox rules, instruction files (CLAUDE.md, AGENTS.md), the environment, reminders. The view takes the system prompt and the user messages apart and shows each part as a section with a title (Base prompt, Skills, Permissions, Environment, the instruction file's path, the tag for others), a coloured label for its kind (agent prompt, instructions file, skills, context, reminder, summary), its size in characters and estimated tokens and, while collapsed, its first line. What the agent added starts collapsed; what you or the model wrote is always shown. Expand all and Collapse all work per block, a section you open stays open in the next call you select, and ⧉ copies a part.

The view scrolls as one: texts have no scroll boxes of their own, the heads of the role and of the open section stay at the top while their text scrolls past, and texts over 300 lines or 30,000 characters show their beginning with Show all. Go to in the header jumps to the system prompt, the history, the answer or the tools.

An error the API answered with (rate limit, invalid request) is shown at the top. Texts longer than 200,000 characters are shortened in this view; the body views show them whole. A stream that ended early says so.

From the view, Cache this answer puts a successful answer into the agent cache, and Try a variant… sends the call again with changes (Try a variant).

In the session list

When a call is done, it gets the flags x-quena-llm (provider/model), x-quena-llm-tokens, x-quena-llm-usage and x-quena-llm-cost. LLM and MCP requests also get x-quena-agent, the agent or SDK that sent them, read from the User-Agent (Claude Code 2.0.14, Codex 0.46.0, Gemini CLI, Cursor, GitHub Copilot, OpenAI SDK (Python) …; for clients Quena does not know, the first word of the User-Agent, such as my-app/1.2). The flags are kept in .saz archives. LLM calls in archives from other tools (without Quena's flags) get them when the Agents panel opens.

  • Columns LLM, Tokens, Cost and Agent (right-click the column headers); they sort. The column Conversation is described on Agent conversations.
  • Group by → LLM model puts the calls of each model together, Group by → Agent (Claude Code, Codex …) those of each agent.
  • Filters: llm ~ claude, llm == "OpenAI/gpt-4o", tokens > 10000, agent ~ "claude code" (see syntax).
  • Statistics lists calls, tokens and estimated cost per model for the selection.

Costs

The cost is an estimate: tokens times the model's list price. Input read from the provider's cache is charged at the cached price, input written to it at the cache-write price, and the rest at the input price. Discounts and batch prices are not known, and models without a price show cost unknown. Answers from Quena's agent cache cost nothing and are not added up. Prices come from three places, and the first that knows the model wins (Settings → Bodies & Storage → LLM prices shows all three):

  1. Your own prices in llm-prices.json in the data directory (Create llm-prices.json / Edit llm-prices.json opens it). US dollars per million tokens, by model name prefix (the longest prefix wins). cacheRead and cacheWrite default to the input price; context, the model's context window in tokens, is optional:

    {
      "gpt-5.1": { "input": 1.25, "output": 10, "cacheRead": 0.125 },
      "claude-opus-4-5": { "input": 5, "output": 25, "cacheRead": 0.5, "cacheWrite": 6.25 },
      "my-model": { "input": 0.5, "output": 1.5, "context": 128000 },
      "llama": { "input": 0, "output": 0 }
    }
    

    Changes count from the next call on, without a restart. A file that is not valid JSON is reported in the settings, the log and the LLM view; its prices are not used until it is fixed.

  2. The fetched price list: Fetch prices / Update prices downloads LiteLLM's price list (MIT licence, several hundred models of OpenAI, Anthropic, Google, Mistral, DeepSeek, Groq, Bedrock, Azure …) through Quena's upstream settings and keeps it in llm-prices-litellm.json in the data directory. This is the only time Quena asks for prices, and only when clicked; Remove list goes back to the other two. A model matches by its name with a date or release tag at most (gpt-4o-2024-08-06 → gpt-4o).

  3. Built-in list prices of common OpenAI, Anthropic, Google, Mistral and DeepSeek models as their providers published them, each with the month it was taken (the cost's tooltip says built-in list prices, 2025 or 2026-10), matched like the fetched list (a newer claude-opus-4-7 does not get the price of claude-opus-4). Prices change: for current ones fetch the price list or add your own.

Model names are compared without a provider path (openai/gpt-4o) and without Bedrock's region, vendor and version (us.anthropic.claude-sonnet-4-5-20250929-v1:0 → claude-sonnet-4-5-20250929).

Replay answers without calling the model

To test an application without paying for (or waiting on) the model, answer its calls from recorded sessions: select them and use Mock Rules → Mocks from Sessions… (see mocks from a capture). With Match request bodies each prompt gets its own recorded answer (JSON compared regardless of key order); without it, In recorded order answers the calls one after the other. Streamed answers are replayed as recorded.

To answer an agent's repeated calls automatically, or to run an agent again against a recorded run, use the agent cache and Freeze a run for replays.

For AI agents

Over Quena's MCP server, get_llm_call returns a call taken apart the same way; the session rows carry llm and tokens. Unless agents may see secrets, the bodies are redacted first (credentials and secret fields), as for get_session. The tools for conversations are listed on Agent conversations.