mirror of
https://github.com/discourse/discourse.git
synced 2026-08-11 02:59:07 +08:00
Apply token-budgeted agent execution and context compaction to all AI agent runs instead of gating it behind execution_mode. Remove the legacy fixed-limit agent settings, keep compression_threshold defaulted to 80, and trim prompt history from the latest compression checkpoint. Why: Preserving a stable compressed context across turns keeps important conversation state available while allowing newer messages to append to a consistent prefix. That improves cache reuse and avoids repeatedly throwing away useful context just to stay under model limits. Compaction shape: before: turn 1 [full -> compact] | turn 2 [full -> compact] after: [compact checkpoint] -> turn 1 -> turn 2 -> ... --------- Co-authored-by: Rafael Silva <xfalcox@gmail.com>
71 lines
2.1 KiB
Ruby
Vendored
71 lines
2.1 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
module DiscourseAi
|
|
module Completions
|
|
class TokenUsageTracker
|
|
def initialize(base_total: nil, base_request: nil, base_response: nil)
|
|
@mutex = Mutex.new
|
|
if base_request.nil? && base_response.nil?
|
|
total = base_total.to_i
|
|
initial_request = total / 2
|
|
@request = initial_request
|
|
@response = total - initial_request
|
|
else
|
|
if !base_total.nil?
|
|
raise ArgumentError, "base_total cannot be combined with base_request/base_response"
|
|
end
|
|
if base_request.nil? || base_response.nil?
|
|
raise ArgumentError, "base_request and base_response must both be provided"
|
|
end
|
|
|
|
@request = base_request.to_i
|
|
@response = base_response.to_i
|
|
end
|
|
end
|
|
|
|
def add_from_audit_log(log)
|
|
# request_tokens = non-cached input (already excludes cached)
|
|
# cache_write_tokens = newly cached (full cost)
|
|
# cache_read_tokens = served from cache (1/10 cost)
|
|
request =
|
|
log.request_tokens.to_i + log.cache_write_tokens.to_i +
|
|
(log.cache_read_tokens.to_i * 0.1).to_i
|
|
response = log.response_tokens.to_i
|
|
|
|
request = estimate_tokens(raw_payload(log, :raw_request_payload)) if request <= 0
|
|
response = estimate_tokens(raw_payload(log, :raw_response_payload)) if response <= 0
|
|
|
|
add_effective(request: request, response: response)
|
|
end
|
|
|
|
def raw_payload(log, name)
|
|
log.public_send(name) if log.respond_to?(name)
|
|
end
|
|
|
|
def estimate_tokens(payload)
|
|
return 0 if payload.blank?
|
|
|
|
(payload.to_s.bytesize / 3.0).ceil
|
|
end
|
|
|
|
def add_effective(request:, response:)
|
|
@mutex.synchronize do
|
|
@request += request.to_i
|
|
@response += response.to_i
|
|
end
|
|
end
|
|
|
|
def request
|
|
@mutex.synchronize { @request }
|
|
end
|
|
|
|
def response
|
|
@mutex.synchronize { @response }
|
|
end
|
|
|
|
def total
|
|
@mutex.synchronize { @request + @response }
|
|
end
|
|
end
|
|
end
|
|
end
|