Add AWS Bedrock three-tier gateway routing (LiteLLM via Colin)

Routes AI tools across three tiers based on task complexity:
- Azure GPT-4o-mini always: redact, translate, timeline-basic, search-legal (mechanical tasks)
- Claude Haiku 4.5 (Bedrock): ask, summarize, timeline-deep, citations (Norwegian nuance)
- Claude Sonnet 4.6 (Bedrock): korrespond, legal-analysis, deep-research, barnevernet-analyze,
  discrepancy-find, advocate (public-facing legal output)

No AWS credentials in app — credentials live in LiteLLM on Colin (same as nova-lite).
Rollback: DBN_BEDROCK_ENABLED=false in .env, no code push needed.

Includes extended thinking support for Pro deep-research via chatWithThinking().
Claude Opus 4.7 constant added for future premium tier (needs litellm_config.yaml entry).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-25 15:22:48 +02:00
parent 17ad54cf36
commit 8a11001bff
11 changed files with 520 additions and 43 deletions
+25 -22
View File
@@ -21,7 +21,7 @@ final class ToolModels
public static function engineForUser(int $userId, string $requestedEngine): string
{
$valid = ['nova_lite', 'azure_mini', 'azure_full', 'gpu', 'regex'];
$valid = ['nova_lite', 'azure_mini', 'azure_full', 'gpu', 'regex', 'claude_haiku', 'claude_sonnet'];
$requestedEngine = in_array($requestedEngine, $valid, true) ? $requestedEngine : 'azure_mini';
if ($userId <= 0) {
@@ -37,7 +37,7 @@ final class ToolModels
public static function timelineRoute(int $userId, string $requestedEngine, string $text): array
{
$valid = ['nova_lite', 'azure_mini', 'azure_full'];
$valid = ['nova_lite', 'azure_mini', 'azure_full', 'claude_haiku', 'claude_sonnet'];
$requestedEngine = in_array($requestedEngine, $valid, true) ? $requestedEngine : 'azure_mini';
$tierEngine = self::engineForUser($userId, $requestedEngine);
$charCount = mb_strlen($text, 'UTF-8');
@@ -122,60 +122,63 @@ final class ToolModels
public static function timelineEngineLimit(string $engine): int
{
return match ($engine) {
'nova_lite' => self::TIMELINE_QUICK_CHAR_LIMIT,
'azure_mini' => self::TIMELINE_STANDARD_CHAR_LIMIT,
default => self::TIMELINE_DEEP_CHAR_LIMIT,
'nova_lite', 'claude_haiku' => self::TIMELINE_QUICK_CHAR_LIMIT,
'azure_mini' => self::TIMELINE_STANDARD_CHAR_LIMIT,
default => self::TIMELINE_DEEP_CHAR_LIMIT,
};
}
public static function timelineChunkSize(string $engine): int
{
return match ($engine) {
'nova_lite' => 10000,
'azure_mini' => 16000,
default => 30000,
'nova_lite', 'claude_haiku' => 10000,
'azure_mini' => 16000,
default => 30000,
};
}
public static function timelineEngineMaxChars(string $engine): int
{
return match ($engine) {
'nova_lite' => self::TIMELINE_QUICK_MAX_CHARS,
'azure_mini' => self::TIMELINE_STANDARD_MAX_CHARS,
default => self::TIMELINE_DEEP_MAX_CHARS,
'nova_lite', 'claude_haiku' => self::TIMELINE_QUICK_MAX_CHARS,
'azure_mini' => self::TIMELINE_STANDARD_MAX_CHARS,
default => self::TIMELINE_DEEP_MAX_CHARS,
};
}
public static function timelineCreditsForSize(string $engine, int $charCount): int
{
return match ($engine) {
'nova_lite' => $charCount <= self::TIMELINE_QUICK_CHAR_LIMIT ? 1 : 2,
'azure_mini' => $charCount <= self::TIMELINE_STANDARD_CHAR_LIMIT ? 1 : ($charCount <= 180000 ? 2 : 3),
default => $charCount <= self::TIMELINE_DEEP_CHAR_LIMIT ? 2 : ($charCount <= 350000 ? 4 : 6),
'nova_lite', 'claude_haiku'
=> $charCount <= self::TIMELINE_QUICK_CHAR_LIMIT ? 1 : 2,
'azure_mini'
=> $charCount <= self::TIMELINE_STANDARD_CHAR_LIMIT ? 1 : ($charCount <= 180000 ? 2 : 3),
default
=> $charCount <= self::TIMELINE_DEEP_CHAR_LIMIT ? 2 : ($charCount <= 350000 ? 4 : 6),
};
}
public static function timelineAdvertisedCredits(string $engine): int
{
return $engine === 'azure_full' ? 2 : 1;
return in_array($engine, ['azure_full', 'claude_sonnet'], true) ? 2 : 1;
}
public static function timelineEngineLabel(string $engine): string
{
return match ($engine) {
'nova_lite' => 'Quick',
'azure_full' => 'Deep',
default => 'Standard',
'nova_lite', 'claude_haiku' => 'Quick',
'azure_full', 'claude_sonnet' => 'Deep',
default => 'Standard',
};
}
private static function timelineEngineRank(string $engine): int
{
return match ($engine) {
'nova_lite' => 1,
'azure_mini' => 2,
'azure_full' => 3,
default => 0,
'nova_lite', 'claude_haiku' => 1,
'azure_mini' => 2,
'azure_full', 'claude_sonnet' => 3,
default => 0,
};
}
}