Class: Docforge::Client

Inherits:
Object
  • Object
show all
Includes:
HTTParty
Defined in:
lib/docforge/client.rb

Overview

Thin wrapper around the Anthropic Messages API. Docs: docs.anthropic.com/en/api/messages

We use *tool-use forcing* to guarantee a structurally valid JSON payload. Free-form text responses occasionally include unescaped quotes inside long string values; tool_use bypasses that entirely because the API validates the tool input against a JSON schema before returning.

Constant Summary collapse

API_VERSION =
"2023-06-01"
MAX_TOKENS =
8000
TOOL_NAME =
"emit_brief"
RAW_DUMP =
"/tmp/docforge-last-response.json"

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ Client

Returns a new instance of Client.



23
24
25
# File 'lib/docforge/client.rb', line 23

def initialize(config)
  @config = config
end

Instance Method Details

#generate_brief(system_prompt:, user_message:) ⇒ Object

Raises:



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/docforge/client.rb', line 27

def generate_brief(system_prompt:, user_message:)
  body = {
    model: @config.model,
    max_tokens: MAX_TOKENS,
    system: system_prompt,
    tools: [{
      name: TOOL_NAME,
      description: "Emit the structured feature brief.",
      input_schema: { type: "object", additionalProperties: true }
    }],
    tool_choice: { type: "tool", name: TOOL_NAME },
    messages: [{ role: "user", content: user_message }]
  }

  response = self.class.post(
    "/v1/messages",
    headers: {
      "x-api-key" => @config.api_key,
      "anthropic-version" => API_VERSION,
      "content-type" => "application/json"
    },
    body: body.to_json,
    timeout: 120
  )

  raise ApiError, "Anthropic API #{response.code}: #{response.body}" unless response.success?

  # Dump raw response immediately so tokens are never wasted on a parse fail.
  begin
    File.write(RAW_DUMP, JSON.pretty_generate(response.parsed_response))
  rescue StandardError
    # best-effort
  end

  extract_tool_input(response.parsed_response)
end