Skip to content

Anthropic: Fix system prompt (use plain text instead of serialized JSON) #302

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 30, 2025

Conversation

MichaelHoste
Copy link
Contributor

@MichaelHoste MichaelHoste commented Jul 28, 2025

What this does

When migrating from ruby-openai, I had some issues getting the same responses in my Anthropic test suite.

After some digging, I observed that the Anthropic requests send the system context as serialized JSON instead of a plain string like described in the API reference:

{
  :system => "{type:\n        \"text\", text: \"You must include the exact phrase \\\"XKCD7392\\\" somewhere\n        in your response.\"}",
  [...]
}

instead of :

{
  :system => "You must include the exact phrase \"XKCD7392\" somewhere in your response.",
  [...]
}

It works quite well (the model still understands it) but it uses more tokens than needed. It could also mislead the model in interpreting the system prompt.

This PR fixed it. I also took the initiative to make the temperature an optional parameter (just like with OpenAI). I hope it's not too much for a single PR, but since I was already re-recording the cassettes, I figured it would be easier.

I'm sorry but I don't have any API key for Bedrock/OpenRouter. I only recorded the main Anthropic cassettes.

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Performance improvement

Scope check

  • I read the Contributing Guide
  • This aligns with RubyLLM's focus on LLM communication
  • This isn't application-specific logic that belongs in user code
  • This benefits most users, not just my specific use case

Quality check

  • I ran overcommit --install and all hooks pass
  • I tested my changes thoroughly
  • I updated documentation if needed
  • I didn't modify auto-generated files manually (models.json, aliases.json)

API changes

  • Breaking change
  • New public methods/classes
  • Changed method signatures
  • No API changes

Related issues

@tpaulshippy
Copy link
Contributor

Just a note that #234 also fixes this issue but in a slightly different way. https://docs.anthropic.com/en/api/messages#body-system shows that you can provide a string or an array of objects. When caching system prompts, you have to use the array to provide the cache_control attribute, so I switched to providing the array instead of the string. Without that PR though, this PR will definitely clean up the string.

@MichaelHoste
Copy link
Contributor Author

Thanks @tpaulshippy !

If #234 solves the issue and is merged soon, it's good to me 🙂 I noticed in the cassettes that the JSON was not serialized anymore so that should be ok.

But since it's a bug, if #234 still need some time to be merged, this one could maybe be merged first? There will not be a lot of conflicts. And I still think that the temperature should be an optional parameter for Anthropic (same as OpenAI).

@tpaulshippy
Copy link
Contributor

Agreed. Would love to see this get merged ASAP.

Copy link

codecov bot commented Jul 30, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.61%. Comparing base (8c9d990) to head (852b330).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #302   +/-   ##
=======================================
  Coverage   86.61%   86.61%           
=======================================
  Files          79       79           
  Lines        3123     3124    +1     
  Branches      611      612    +1     
=======================================
+ Hits         2705     2706    +1     
  Misses        418      418           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Owner

@crmne crmne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@crmne crmne merged commit b1238d5 into crmne:main Jul 30, 2025
14 checks passed
tpaulshippy pushed a commit to tpaulshippy/ruby_llm that referenced this pull request Aug 3, 2025
…ON) (crmne#302)

## What this does

When migrating from
[ruby-openai](https://github.com/alexrudall/ruby-openai), I had some
issues getting the same responses in my Anthropic test suite.

After some digging, I observed that the Anthropic requests send the
`system context` as serialized JSON instead of a plain string like
described in the [API
reference](https://docs.anthropic.com/en/api/messages#body-system):

```ruby
{
  :system => "{type:\n        \"text\", text: \"You must include the exact phrase \\\"XKCD7392\\\" somewhere\n        in your response.\"}",
  [...]
}
```

instead of :  

```ruby
{
  :system => "You must include the exact phrase \"XKCD7392\" somewhere in your response.",
  [...]
}
```

It works quite well (the model still understands it) but it uses more
tokens than needed. It could also mislead the model in interpreting the
system prompt.

This PR fixed it. I also took the initiative to make the temperature an
optional parameter ([just like with
OpenAI](https://github.com/crmne/ruby_llm/blob/main/lib/ruby_llm/providers/openai/chat.rb#L21-L22)).
I hope it's not too much for a single PR, but since I was already
re-recording the cassettes, I figured it would be easier.

I'm sorry but I don't have any API key for Bedrock/OpenRouter. I only
recorded the main Anthropic cassettes.

## Type of change

- [x] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation
- [ ] Performance improvement

## Scope check

- [x] I read the [Contributing
Guide](https://github.com/crmne/ruby_llm/blob/main/CONTRIBUTING.md)
- [x] This aligns with RubyLLM's focus on **LLM communication**
- [x] This isn't application-specific logic that belongs in user code
- [x] This benefits most users, not just my specific use case

## Quality check

- [x] I ran `overcommit --install` and all hooks pass
- [x] I tested my changes thoroughly
- [ ] I updated documentation if needed
- [x] I didn't modify auto-generated files manually (`models.json`,
`aliases.json`)

## API changes

- [ ] Breaking change
- [ ] New public methods/classes
- [ ] Changed method signatures
- [ ] No API changes

## Related issues

<!-- Link issues: "Fixes crmne#123" or "Related to crmne#123" -->

---------

Co-authored-by: Carmine Paolino <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants