Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion juce_llm/llm/LLMTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ struct ProviderConfig {
juce::String apiKey;
juce::String model;

int maxTokens = 4096; // max output tokens (0 = provider default)
int maxTokens = 0; // max output tokens (0 = provider default — don't send a cap)

// Provider-specific options
bool noTemperature = false; // GPT-5 doesn't support temperature
Expand Down
12 changes: 5 additions & 7 deletions juce_llm/llm/providers/AnthropicClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ juce::String AnthropicClient::buildRequestBody(const Request& request) const {

auto* payload = new juce::DynamicObject();
payload->setProperty("model", config_.model);
int maxTok = request.maxTokens > 0 ? request.maxTokens : (config_.maxTokens > 0 ? config_.maxTokens : 4096);
int maxTok = request.maxTokens > 0 ? request.maxTokens
: (config_.maxTokens > 0 ? config_.maxTokens : 4096);
payload->setProperty("max_tokens", maxTok);
payload->setProperty("temperature", (double)request.temperature);
payload->setProperty("messages", messagesArray);
Expand All @@ -31,12 +32,9 @@ juce::String AnthropicClient::buildRequestBody(const Request& request) const {
payload->setProperty("system", systemArray);
}

// Output effort — low/medium/high/max (similar to OpenAI reasoning_effort)
if (config_.reasoningEffort.isNotEmpty()) {
auto* outputConfig = new juce::DynamicObject();
outputConfig->setProperty("effort", config_.reasoningEffort);
payload->setProperty("output_config", juce::var(outputConfig));
}
// NOTE: Anthropic's Messages API does not accept an `effort` / `output_config`
// field — that is an OpenAI-ism. `reasoningEffort` is deliberately ignored here.
// (Extended thinking uses a separate `thinking` block on models that support it.)

// App identification for abuse tracking
if (config_.userAgent.isNotEmpty()) {
Expand Down
13 changes: 13 additions & 0 deletions juce_llm/llm/providers/OpenAIResponsesClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,17 @@ Response OpenAIResponsesClient::parseResponseBody(const juce::String& jsonString
return response;
}

/** Responses API SSE chunks look like:
data: {"type":"response.output_text.delta","delta":"token","item_id":"..."}
data: {"type":"response.completed", ...}
The base LLMClient class only reads "data: " lines, so we just need to pull
the "delta" field out of the JSON when the event type matches. */
juce::String OpenAIResponsesClient::parseStreamChunk(const juce::String& dataLine) const {
auto json = juce::JSON::parse(dataLine);
auto type = json["type"].toString();
if (type == "response.output_text.delta")
return json["delta"].toString();
return {};
}

} // namespace llm
1 change: 1 addition & 0 deletions juce_llm/llm/providers/OpenAIResponsesClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class OpenAIResponsesClient : public LLMClient {
juce::String getEndpointUrl() const override;
juce::StringPairArray getHeaders() const override;
Response parseResponseBody(const juce::String& jsonString) const override;
juce::String parseStreamChunk(const juce::String& dataLine) const override;
};

} // namespace llm
Loading