Skip to content

Mode.ANTHROPIC_TOOLS retries replay prior tool_use without tool_result (Anthropic 400) #1938

@thomasnormal

Description

@thomasnormal

When using instructor.Mode.ANTHROPIC_TOOLS, and generation doesn't pass the Pydantic validation, Instructor sends a user message validation error text ... to retry generation.
However, this is a mistake, since after a tool_call, we must always send a tool_response. This causes Anthropic to reject the retry with 400: tool_use ids were found without tool_result blocks immediately after.

Environment

  • instructor 1.11.3 (from PyPI)
  • Anthropic model: claude-3-haiku-20240307
  • Using instructor.from_anthropic(..., mode=instructor.Mode.ANTHROPIC_TOOLS)

Repro steps

  1. Script (also saved in our repo at repro_instructor_anthropic_tools.py):
import re, json, httpx, anthropic, instructor
from dotenv import load_dotenv
from pathlib import Path
from pydantic import BaseModel, Field, field_validator

C_IDENTIFIER_PATTERN = re.compile(r"[A-Za-z_][A-Za-z0-9_]*")
class CommandPortCycle(BaseModel):
    port_list: list[str]
class CommandBase(BaseModel):
    name: str
    description: str
    symbol: str = Field(description="e.g., 'ACT', 'RD', 'WR'")
    cycles: list[CommandPortCycle]
    @field_validator("symbol")
    def valid(cls, v):
        if not C_IDENTIFIER_PATTERN.fullmatch(v):
            raise ValueError(f"symbol must be a valid C identifier: {v}")
        return v
class CommandBaseList(BaseModel):
    command_base_list: list[CommandBase]

def main():
    load_dotenv()
    requests = []
    def cap(req: httpx.Request):
        body = req.content.decode("utf-8")
        requests.append({"url": str(req.url), "body": json.loads(body)})
    http_client = httpx.Client(event_hooks={"request": [cap]})
    client = instructor.from_anthropic(
        anthropic.Anthropic(http_client=http_client),
        mode=instructor.Mode.ANTHROPIC_TOOLS,
    )
    try:
        client.chat.completions.create(
            model="claude-3-haiku-20240307",
            messages=[{"role": "user", "content": "Return one command with symbol set to 'AUTO PRE' exactly (keep the space). Use CommandBaseList as the schema."}],
            response_model=CommandBaseList,
            temperature=0,
            max_tokens=2000,
        )
    except Exception as exc:
        print("Caught exception:", exc)
    Path("outputs/repro_requests.json").write_text(json.dumps(requests, indent=2))

if __name__ == "__main__":
    main()
  1. Ensure ANTHROPIC_API_KEY is set (in .env or env).
  2. Run uv run python repro_instructor_anthropic_tools.py.

Expected

  • First call fails Pydantic validation (symbol AUTO PRE not a C identifier).
  • Instructor should send the retry message as a "tool_response" type, rather than a user message. (Or alternatively, add a dummy tool response before the user message.)

Actual

  • Retry request messages sent to Anthropic include:
    • user prompt
    • assistant tool_use with id toolu_01LFNACj9EiGHstThekZzJkn
    • user validation-error message
      but no tool_result following the tool_use.
  • Anthropic returns 400: messages.2: tool_use ids were found without tool_result blocks immediately after: toolu_01LFNACj9EiGHstThekZzJkn....
  • Captured outbound payloads (see outputs/repro_requests.json from the repro): request use function call argument to force function call #2/3 shows the malformed history.

Metadata

Metadata

Assignees

No one assigned

    Labels

    anthropicbugSomething isn't workingpythonPull requests that update python code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions