KI-Backend: Umfangs-Umschalter + Prompt Caching (4.5.15/4.5.16)
Umfangs-Umschalter: Checkbox "Auch bestehende Stundeninhalte anpassen" im AiAssistDialog steuert, ob die KI bestehende Stunden inhaltlich ändern darf oder die Einheit nur um neue Stunden erweitern soll. Zweifach durchgesetzt (Systemprompt + hartes client-seitiges Verwerfen in ApplyResponse), nicht nur der KI-Antwort vertraut. Dabei auch einen Bug gefixt: eine bereits "Durchgeführt" markierte Stunde wurde durch eine übernommene KI-Änderung stillschweigend auf "Geplant" zurückgesetzt. Prompt Caching: der Systemprompt ist jetzt vollständig statisch (Voraussetzung für Caching) und wird von AnthropicProvider.php als "cache_control: ephemeral" markiert — wiederholte Anfragen innerhalb der 5-Minuten-TTL zahlen nur den reduzierten Cache-Read-Preis. transactions-Tabelle und Preistabelle um Cache-Token-Spalten erweitert, Migration für bereits deployte Installationen beigelegt.
This commit is contained in:
@@ -3,7 +3,17 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/ProviderInterface.php';
|
||||
|
||||
/** Ruft die Anthropic Messages API direkt per curl auf — bewusst ohne SDK-Abhängigkeit. */
|
||||
/**
|
||||
* Ruft die Anthropic Messages API direkt per curl auf — bewusst ohne SDK-Abhängigkeit.
|
||||
*
|
||||
* Der Systemprompt wird als eigener, mit "cache_control" markierter Content-Block gesendet
|
||||
* (Prompt Caching, siehe TODO 4.5.9-Nachtrag) statt als einfacher String — er ist über alle
|
||||
* Anfragen hinweg identisch (siehe plan.php), also ein idealer Kandidat: wiederholte Aufrufe
|
||||
* innerhalb der Cache-TTL (Anthropic-Standard: 5 Minuten) zahlen dafür nur den stark reduzierten
|
||||
* "Cache-Read"-Preis statt des vollen Input-Preises. Der erste Aufruf nach TTL-Ablauf zahlt einen
|
||||
* kleinen Aufpreis fürs Neuschreiben des Caches ("cache_creation_input_tokens") — siehe
|
||||
* plan.php für die Abrechnung beider Fälle.
|
||||
*/
|
||||
class AnthropicProvider implements ProviderInterface
|
||||
{
|
||||
public function __construct(private string $apiKey, private string $model) {}
|
||||
@@ -22,7 +32,9 @@ class AnthropicProvider implements ProviderInterface
|
||||
CURLOPT_POSTFIELDS => json_encode([
|
||||
'model' => $this->model,
|
||||
'max_tokens' => $maxTokens,
|
||||
'system' => $systemPrompt,
|
||||
'system' => [
|
||||
['type' => 'text', 'text' => $systemPrompt, 'cache_control' => ['type' => 'ephemeral']],
|
||||
],
|
||||
'messages' => [['role' => 'user', 'content' => $userContent]],
|
||||
]),
|
||||
CURLOPT_TIMEOUT => 90,
|
||||
@@ -58,6 +70,8 @@ class AnthropicProvider implements ProviderInterface
|
||||
'content' => $text,
|
||||
'inputTokens' => (int) ($data['usage']['input_tokens'] ?? 0),
|
||||
'outputTokens' => (int) ($data['usage']['output_tokens'] ?? 0),
|
||||
'cacheCreationInputTokens' => (int) ($data['usage']['cache_creation_input_tokens'] ?? 0),
|
||||
'cacheReadInputTokens' => (int) ($data['usage']['cache_read_input_tokens'] ?? 0),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ class FakeProvider implements ProviderInterface
|
||||
]),
|
||||
'inputTokens' => 42,
|
||||
'outputTokens' => 17,
|
||||
'cacheCreationInputTokens' => 0,
|
||||
'cacheReadInputTokens' => 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,12 @@ declare(strict_types=1);
|
||||
interface ProviderInterface
|
||||
{
|
||||
/**
|
||||
* @return array{content: string, inputTokens: int, outputTokens: int}
|
||||
* cacheCreationInputTokens/cacheReadInputTokens sind für Provider ohne Prompt-Caching-
|
||||
* Unterstützung einfach immer 0 (siehe FakeProvider) — plan.php rechnet damit trotzdem
|
||||
* korrekt, ohne je nach Provider zu unterscheiden.
|
||||
*
|
||||
* @return array{content: string, inputTokens: int, outputTokens: int,
|
||||
* cacheCreationInputTokens: int, cacheReadInputTokens: int}
|
||||
*/
|
||||
public function sendMessage(string $systemPrompt, string $userContent, int $maxTokens): array;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user