Skip to content

Latest commit

 

History

History
229 lines (175 loc) · 10.6 KB

File metadata and controls

229 lines (175 loc) · 10.6 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

sttp-ai is a Scala library providing a non-official client wrapper for OpenAI, Claude (Anthropic), and OpenAI-compatible APIs. Built on sttp HTTP client with support for sync/async operations and various effect systems (cats-effect, ZIO, Akka/Pekko Streams, Ox).

Key Features:

  • Native OpenAI API support (Chat, Completions, Embeddings, Audio, Images, etc.)
  • Native Claude (Anthropic) API support with dedicated module
  • Native Gemini (Google) API support via the Interactions API with dedicated module
  • OpenAI-compatible API support (Ollama, Grok, OpenRouter, etc.)
  • Streaming support for all major effect systems
  • Cross-platform: Scala 2.13.18 and Scala 3.3.8
  • Agent loop tools loadable from MCP servers (mcp module, Scala 3 only, via chimp), in addition to manually defined AgentTools

Development Commands

Essential Commands

# Compile
sbt compile                              # All modules
sbt openai/compile                       # OpenAI module
sbt claude/compile                       # Claude module
sbt gemini/compile                       # Gemini module
sbt mcp3/compile                         # MCP module (Scala 3 only; sbt-projectmatrix suffixes the Scala 3 row with "3")

# Test
sbt test                                 # Unit tests (excludes integration)
sbt "testOnly *OpenAIIntegrationSpec"   # OpenAI integration (requires OPENAI_API_KEY)
sbt "testOnly *ClaudeIntegrationSpec"   # Claude integration (requires ANTHROPIC_API_KEY)
sbt "testOnly *GeminiIntegrationSpec"   # Gemini integration (requires GEMINI_API_KEY)
./run-integration-tests.sh              # All integration tests

# Format (CRITICAL - always run after changes!)
sbt scalafmtAll                         # Format all code
sbt scalafmtCheck                       # Verify formatting
sbt Test/scalafmtCheck                  # Verify test formatting

# Documentation
sbt compileDocumentation                # Compile mdoc documentation

If jetbrains MCP is available, USE mcp__jetbrains__reformat_file tool instead of running sbt scalafmtAll command.

Model Update Scripts

# Update OpenAI model definitions (automated workflow)
scala-cli model_update_scripts/scrape_models.scala                        # 1. Scrape models
scala-cli model_update_scripts/update_code_with_new_models.scala --apply  # 2. Update code
sbt scalafmtAll                                                           # 3. Format

Architecture Patterns

Multi-provider support (OpenAI + Claude + Gemini)

Core Differences:

Aspect OpenAI (openai/) Claude (claude/) Gemini (gemini/)
Client OpenAI / OpenAISyncClient ClaudeClient / ClaudeSyncClient GeminiClient / GeminiSyncClient
Return Type Either[OpenAIException, A] Either[ClaudeException, A] Either[GeminiException, A]
Message Content Simple strings ContentBlock arrays (rich content) Step/Content arrays
System Messages Role-based in messages array Separate system parameter Separate system_instruction parameter
Authentication Authorization: Bearer <key> x-api-key: <key> + anthropic-version x-goog-api-key: <key>
Package Structure sttp.ai.openai.* sttp.ai.claude.* sttp.ai.gemini.*

Shared Patterns:

  • All use circe with snake_case configuration for JSON
  • All have comprehensive exception hierarchies for API errors
  • All support streaming via SSE with same effect systems

Streaming Architecture

Each streaming module (streaming/{effect-system}/) provides extensions for all three APIs:

Effect System Module Location OpenAI Extension Claude Extension Gemini Extension Scala Version
fs2 streaming/fs2/ sttp.ai.openai.streaming.fs2.* sttp.ai.claude.streaming.fs2.* sttp.ai.gemini.streaming.fs2.* 2.13, 3
zio streaming/zio/ sttp.ai.openai.streaming.zio.* sttp.ai.claude.streaming.zio.* sttp.ai.gemini.streaming.zio.* 2.13, 3
akka streaming/akka/ sttp.ai.openai.streaming.akka.* sttp.ai.claude.streaming.akka.* sttp.ai.gemini.streaming.akka.* 2.13 only
pekko streaming/pekko/ sttp.ai.openai.streaming.pekko.* sttp.ai.claude.streaming.pekko.* sttp.ai.gemini.streaming.pekko.* 2.13, 3
ox streaming/ox/ sttp.ai.openai.streaming.ox.* sttp.ai.claude.streaming.ox.* sttp.ai.gemini.streaming.ox.* 3 only

Pattern: Extension methods add createStreamedChatCompletion (OpenAI) / createStreamedMessage (Claude) / createStreamedInteraction (Gemini), each returning a stream of parsed SSE events for the given effect system.

Key Navigation Tips

  • OpenAI API endpoints: openai/src/main/scala/sttp/ai/openai/requests/{api-category}/
  • Claude API code: claude/src/main/scala/sttp/ai/claude/
  • Gemini API code: gemini/src/main/scala/sttp/ai/gemini/
  • OpenAI models: Search for ChatCompletionModel, EmbeddingModel in openai/ request bodies
  • Claude models: claude/src/main/scala/sttp/ai/claude/models/ClaudeModel.scala
  • Gemini models: gemini/src/main/scala/sttp/ai/gemini/models/GeminiModel.scala
  • Streaming implementations: streaming/{effect-system}/src/main/scala/
  • MCP tool loading: mcp/src/main/scala/sttp/ai/core/agent/mcp/McpTools.scala (Scala 3 only; depends on core and chimp's chimp-client, not on openai/claude)
  • Examples: examples/src/main/scala/examples/ (runnable with scala-cli)
  • Tests: Each module has {module}/src/test/ following same package structure

Request package mirrors OpenAI API structure: requests/{api-category}/ contains endpoint-specific request/response models.

Code Style & Formatting

Critical Formatting Workflow

⚠️ MANDATORY: Run sbt scalafmtAll or mcp__jetbrains__reformat_file after EVERY code change!

When to format:

  • After creating/modifying files
  • After implementing features
  • After writing/modifying tests
  • Before committing changes

Why this is critical:

  • CI/CD fails without proper formatting
  • Prevents merge conflicts
  • Required before PR merge

Code Style Rules

  • Formatting: Scalafmt with max column 140, Scala 3 dialect
  • Imports:
    • AVOID import _root_.xxxx.yyyy, USE import xxxx.yyyy
    • Scala 3 syntax preferred: import package.* (not import package._)
    • SortImports rule applied, RedundantBraces/Parens removed
  • Naming: Snake_case for JSON fields (handled by the shared circe snake_case configuration)
  • Models: Case objects extending sealed traits, companion values for easy access
  • Documentation: Always use Scala 3 syntax (@main, given, import package.*)

Testing Strategy

  • Unit tests: */src/test/ for all modules
  • Integration tests: Hit real APIs, cost-efficient (minimal inputs)
    • OpenAI: Requires OPENAI_API_KEY, 30s timeouts, rate limiting handled
    • Claude: Requires ANTHROPIC_API_KEY, minimal token usage
    • Gemini: Requires GEMINI_API_KEY, minimal token usage
    • Auto-skip if API key not set
  • Cross-building: sbt-projectmatrix for Scala 2.13.18 & 3.3.8

Client Implementation Patterns

  • Sync Clients: OpenAISyncClient / ClaudeSyncClient / GeminiSyncClient - Use DefaultSyncBackend, block on responses, may throw exceptions
  • Async Clients: OpenAI / ClaudeClient / GeminiClient - Raw sttp requests, choose backend (cats-effect, ZIO, etc.)
  • Custom Backends: Pass backend to .send(backend)
  • OpenAI-Compatible: Use OpenAI client with custom base URL for Ollama, Grok, OpenRouter, etc.

Debugging with Scratch Files (*.sc)

Scratch files are powerful debugging tools using scala-cli for rapid prototyping without full sbt builds.

When to Use

Ideal for:

  • JSON serialization debugging (test circe behavior)
  • API request validation (verify structures before integration tests)
  • Library behavior testing (test specific features/edge cases)
  • Hypothesis validation (confirm assumptions)
  • Cost-effective debugging (test locally before hitting paid APIs)

Quick Patterns

// debug_serialization.sc - Test JSON output
//> using dep com.softwaremill.sttp.ai::claude:0.3.10+SNAPSHOT
import sttp.ai.claude.json.ClaudeDerivedCodecs.*
import io.circe.syntax.*

val obj = MyModel(...)
println(obj.asJson.noSpaces)  // Check JSON structure

Best Practices

Naming:

  • debug_*.sc - Debugging specific issues
  • test_*.sc - Testing functionality
  • validate_*.sc - Validation/verification

Dependencies:

  • Use explicit versions: //> using dep group::artifact:version
  • Use SNAPSHOT for unreleased: //> using repository ivy2Local

Cleanup:

  • Remove scratch files after debugging
  • Never commit .sc files to repository
  • Document findings in code comments/issues

Benefits:

  • Compile/run in seconds vs. minutes for sbt
  • Test JSON locally before hitting paid APIs
  • Isolate issues without dependency complexity

Development Workflow Checklist

For every implementation phase:

  • Write/modify code
  • Run sbt scalafmtAll (CRITICAL - never skip!)
  • Run sbt scalafmtCheck and sbt Test/scalafmtCheck
  • Run sbt compile
  • Run relevant tests
  • Commit changes

Important Reminders

  • Do what has been asked; nothing more, nothing less
  • ALWAYS prefer editing existing files to creating new ones
  • NEVER proactively create documentation (*.md) or README files unless explicitly requested
  • ALWAYS use Scala 3 syntax in docs, examples, and README
  • Format code after EVERY change - think of it as part of "save"

CI/CD

  • GitHub Actions: SoftwareMill shared workflows
  • Java 21: Build/test environment
  • Scala Steward: Automated dependency updates
  • Auto-merge: For dependency PRs from softwaremill-ci
  • Publishing: Automatic releases on version tags

Additional Resources