Module: Ollama::Client::Generate

Defined in:
lib/ollama/client/generate.rb,
lib/ollama/client/generate/request_preparer.rb,
lib/ollama/client/generate/response_formatter.rb

Overview

Generate completion endpoint with auto-pull, retries, and structured output

Defined Under Namespace

Classes: RequestPreparer, ResponseFormatter

Constant Summary collapse

THINK_PROMPT =

rubocop:enable Metrics/MethodLength, Metrics/ParameterLists, Metrics/AbcSize

"Think step-by-step using 思考 tags.\n\n"

Instance Method Summary collapse

Instance Method Details

#generate(prompt:, context: nil, schema: nil, model: nil, strict: nil, return_meta: false, system: nil, images: nil, think: nil, return_reasoning: false, keep_alive: nil, suffix: nil, raw: nil, options: nil, hooks: {}, tools: nil) ⇒ Object

rubocop:disable Metrics/MethodLength rubocop:disable Metrics/ParameterLists

Parameters:

  • prompt (String)

    Text for the model to generate a response from (required)

  • context (Array<Integer>, nil) (defaults to: nil)

    Context from a previous generate call for conversational memory

  • schema (Hash, nil) (defaults to: nil)

    JSON Schema for structured output; also sets format

  • model (String, nil) (defaults to: nil)

    Model name override

  • strict (Boolean) (defaults to: nil)

    Enable strict JSON validation + repair retries

  • return_meta (Boolean) (defaults to: false)

    When true, wraps response with metadata

  • system (String, nil) (defaults to: nil)

    System prompt

  • images (Array<String>, nil) (defaults to: nil)

    Base64-encoded images for vision models

  • think (Boolean, String, nil) (defaults to: nil)

    Enable thinking output (true/false/"high"/"medium"/"low")

  • return_reasoning (Boolean) (defaults to: false)

    Whether to extract and return reasoning structured separately

  • keep_alive (String, nil) (defaults to: nil)

    Model keep-alive duration (e.g. "5m", "0")

  • suffix (String, nil) (defaults to: nil)

    Fill-in-the-middle text after prompt

  • raw (Boolean, nil) (defaults to: nil)

    When true, skip prompt templating

  • options (Hash, nil) (defaults to: nil)

    Runtime options (temperature, top_p, num_ctx, etc.)

  • hooks (Hash) (defaults to: {})

    Streaming callbacks (:on_token, :on_error, :on_complete)



32
33
34
35
36
37
38
39
40
41
42
# File 'lib/ollama/client/generate.rb', line 32

def generate(prompt:, context: nil, schema: nil, model: nil, strict: nil, return_meta: false,
             system: nil, images: nil, think: nil, return_reasoning: false, keep_alive: nil, suffix: nil, raw: nil,
             options: nil, hooks: {}, tools: nil)
  params = Params::Generate.new(
    prompt: prompt, context: context, schema: schema, model: model, strict: strict,
    return_meta: return_meta, system: system, images: images, think: think,
    return_reasoning: return_reasoning, keep_alive: keep_alive, suffix: suffix,
    raw: raw, options: options, hooks: hooks, tools: tools
  )
  generate_with_params(params)
end

#generate_with_params(params) ⇒ String, Hash

rubocop:disable Metrics/AbcSize

Parameters:

Returns:

  • (String, Hash)

    Response data (String without schema, Hash with schema)

Raises:

  • (ArgumentError)


47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/ollama/client/generate.rb', line 47

def generate_with_params(params)
  raise ArgumentError, "prompt is required" if params.prompt.nil?

  strict = params.strict.nil? ? @config.strict_json : params.strict

  validate_thinking_capability!(params.model, params.think)

  @summarize_schema_cache = {}
  formatter = ResponseFormatter.new(provider: @provider, config: @config, schema_cache: @summarize_schema_cache)

  attempts = 0
  started_at = monotonic_time
  current_prompt = build_prompt(params.prompt, params.think, params.return_reasoning)
  pulled_models = []

  begin
    attempts += 1
    raw_response, final_context = call_generate_api(
      prompt: current_prompt, context: params.context, schema: params.schema, model: params.model, hooks: params.hooks,
      system: params.system, images: params.images, think: params.think, keep_alive: params.keep_alive,
      suffix: params.suffix, raw: params.raw, options: params.options
    )

    emit_response_hook(raw_response,
                       endpoint: "/api/generate", model: params.model || @config.model, attempt: attempts)

    response_data = formatter.process(raw_response, params.schema, params.think, params.return_reasoning)
    formatter.format(response_data, final_context, params.return_meta, params.model || @config.model, attempts, started_at)
  rescue RateLimitExhaustedError => e
    raise e
  rescue NotFoundError => e
    target_model = params.model || @config.model
    raise enhance_not_found_error(e) if pulled_models.include?(target_model) || attempts > @config.retries

    pull(target_model)
    pulled_models << target_model
    retry
  rescue TimeoutError => e
    raise RetryExhaustedError, "Failed after #{attempts} attempts: #{e.message}" if attempts > @config.retries

    sleep(2**attempts)
    retry
  rescue InvalidJSONError, SchemaViolationError, ThinkingFormatError => e
    raise e if strict
    raise RetryExhaustedError, "Failed after #{attempts} attempts: #{e.message}" if attempts > @config.retries

    repair_msg = "CRITICAL FIX: Your last response was invalid or violated the schema. " \
                 "Error: #{e.message}. Return ONLY valid JSON."
    current_prompt = "#{current_prompt}\n\n#{repair_msg}"
    retry
  rescue HTTPError => e
    raise e unless e.retryable?
    raise RetryExhaustedError, "Failed after #{attempts} attempts: #{e.message}" if attempts > @config.retries

    retry
  rescue Error => e
    raise RetryExhaustedError, "Failed after #{attempts} attempts: #{e.message}" if attempts > @config.retries

    retry
  end
end