---
name: llm_anthropic
kind: function
lang: cpp
domain: core
version: "1.0.0"
purity: impure
signature: "llm_anthropic::AskResult llm_anthropic::ask(const llm_anthropic::AskInput& in, const std::string& api_key = \"\")"
description: "Minimal Anthropic Messages API client (Claude) via cURL popen. Builds a TQL or SQL expert prompt from column schema + NL question, POSTs to /v1/messages, and returns the extracted code block. Used by the Ask AI panel in data_table."
tags: [llm, anthropic, http, tables, cpp-tables]
uses_functions: []
uses_types:
  - data_table_types_cpp_core
returns: []
returns_optional: false
error_type: "error_go_core"
imports:
  - "core/llm_anthropic.h"
tested: true
tests:
  - "build_request_body: contains model field"
  - "build_request_body: contains messages array"
  - "build_request_body: question appears in user content"
  - "build_request_body: col_names appear in schema block"
  - "build_request_body: custom model overrides default"
  - "build_request_body: SQL mode produces SQL system prompt"
  - "build_request_body: current TQL appears in body"
  - "build_request_body: joinable tables appear in schema"
  - "extract_code_block: extracts lua fence"
  - "extract_code_block: extracts sql fence"
  - "extract_code_block: fallback to plain fence without lang"
  - "extract_code_block: no fence returns stripped raw"
  - "parse_response_text: extracts text from Anthropic response JSON"
  - "parse_response_text: handles escaped newline in text"
  - "parse_response_text: empty text on missing field"
  - "call_api: mock response injection via env var"
  - "ask: mock roundtrip produces code block"
test_file_path: "cpp/tests/test_llm_anthropic.cpp"
file_path: "cpp/functions/core/llm_anthropic.cpp"
params:
  - name: in
    desc: "AskInput with: question (NL), col_names + col_types (table schema), tql_current (optional current TQL to refine), joinable_names (available tables for JOIN), mode (TQL or SQL output), model (empty = claude-sonnet-4-6), max_tokens."
  - name: api_key
    desc: "Anthropic API key. If empty, resolved from env FN_LLM_API_KEY, then `pass anthropic/api-key | head -n1`."
output: "AskResult with: code (extracted Lua or SQL block, no fences), raw (full response text), error (non-empty on failure), tokens_in/tokens_out (0 in v1 — not yet parsed from response)."
---

## Ejemplo

```cpp
#include "core/llm_anthropic.h"

llm_anthropic::AskInput in;
in.question  = "show me total sales grouped by region";
in.col_names = {"region", "amount", "date"};
in.col_types = {data_table::ColumnType::String,
                data_table::ColumnType::Float,
                data_table::ColumnType::Date};
in.mode      = llm_anthropic::OutputMode::TQL;

// API key from env FN_LLM_API_KEY or pass anthropic/api-key
llm_anthropic::AskResult r = llm_anthropic::ask(in);
if (r.error.empty()) {
    // r.code -> "return { version=1, display=\"bar\", ... }"
}
```

## Cuando usarla

Cuando el usuario hace una pregunta en lenguaje natural sobre una tabla en el panel Ask AI de data_table, y necesitas generar TQL o SQL equivalente via Claude. El caller aplica el TQL con `tql_apply` o ejecuta el SQL via DuckDB.

## Gotchas

- **API key obligatoria**: sin FN_LLM_API_KEY ni `pass anthropic/api-key`, `ask` devuelve `error` con el mensaje. No crashea.
- **cURL en PATH**: la implementacion usa `std::system("curl ...")` + archivos temporales. Si `curl` no está disponible, el exit code != 0 y el error se captura en `AskResult.error`.
- **Rate limits**: la API de Anthropic tiene rate limits por tier. Errores 429 aparecen en el JSON de respuesta; actualmente no hay retry — el caller debe manejarlos.
- **Tmpnam deprecated**: usa `std::tmpnam` — genera warning en compiladores modernos. Suficiente para uso interactivo; no apto para alta concurrencia.
- **Test injection**: para tests sin API key, setea `FN_LLM_MOCK_RESPONSE` con el JSON de respuesta Anthropic deseado. `call_api` lo retorna directamente sin hacer HTTP.
- **tokens_in/tokens_out**: siempre 0 en v1 (el campo existe en la respuesta JSON pero no se parsea aún).
- **FN_LLM_ENDPOINT**: override del endpoint para pruebas con proxies o endpoints alternativos.
