Beispiele und anpassungen an Mistral
This commit is contained in:
169
Scripts/ORDS-ai-costs-Endpunkt.sql
Normal file
169
Scripts/ORDS-ai-costs-Endpunkt.sql
Normal file
@@ -0,0 +1,169 @@
|
||||
-- =============================================================================
|
||||
-- ORDS-Endpunkte: KI-Kostenverfolgung (ai-costs)
|
||||
-- Ergänzt das Modul frigosped.dc um Template 17 (POST + GET).
|
||||
--
|
||||
-- Voraussetzung: dc_ai_cost_log-Tabelle und DC_COSTS_PKG bereits vorhanden
|
||||
-- sqlplus user/pass@db @"Scripts/KI-Kostenverfolgung.sql"
|
||||
--
|
||||
-- Deployment:
|
||||
-- sqlplus user/pass@db @"Scripts/ORDS-ai-costs-Endpunkt.sql"
|
||||
--
|
||||
-- Idempotent: bestehende Handler werden zuerst gelöscht.
|
||||
-- =============================================================================
|
||||
SET DEFINE OFF
|
||||
|
||||
BEGIN
|
||||
|
||||
-- Idempotenz: Handler entfernen falls vorhanden
|
||||
BEGIN
|
||||
ORDS.DELETE_HANDLER(
|
||||
p_module_name => 'frigosped.dc',
|
||||
p_uri_template => 'ai-costs',
|
||||
p_method => 'POST'
|
||||
);
|
||||
EXCEPTION WHEN OTHERS THEN NULL;
|
||||
END;
|
||||
|
||||
BEGIN
|
||||
ORDS.DELETE_HANDLER(
|
||||
p_module_name => 'frigosped.dc',
|
||||
p_uri_template => 'ai-costs',
|
||||
p_method => 'GET'
|
||||
);
|
||||
EXCEPTION WHEN OTHERS THEN NULL;
|
||||
END;
|
||||
|
||||
BEGIN
|
||||
ORDS.DELETE_TEMPLATE(
|
||||
p_module_name => 'frigosped.dc',
|
||||
p_uri_template => 'ai-costs'
|
||||
);
|
||||
EXCEPTION WHEN OTHERS THEN NULL;
|
||||
END;
|
||||
|
||||
-- Template anlegen
|
||||
ORDS.DEFINE_TEMPLATE(
|
||||
p_module_name => 'frigosped.dc',
|
||||
p_pattern => 'ai-costs',
|
||||
p_priority => 0,
|
||||
p_comments => 'KI-Kostenprotokoll: POST speichert Aufruf, GET liefert Auswertung'
|
||||
);
|
||||
|
||||
-- POST /api/dc/ai-costs
|
||||
ORDS.DEFINE_HANDLER(
|
||||
p_module_name => 'frigosped.dc',
|
||||
p_pattern => 'ai-costs',
|
||||
p_method => 'POST',
|
||||
p_source_type => ORDS.SOURCE_TYPE_PLSQL,
|
||||
p_comments => '201 Created mit neuer cost_log id',
|
||||
p_source => '
|
||||
DECLARE
|
||||
v_body CLOB;
|
||||
v_new_id NUMBER;
|
||||
BEGIN
|
||||
v_body := :body_text;
|
||||
|
||||
INSERT INTO dc_ai_cost_log (
|
||||
provider, model_name, operation,
|
||||
project_id, catalog_id,
|
||||
prompt_tokens, completion_tokens, total_tokens,
|
||||
cost_eur
|
||||
) VALUES (
|
||||
JSON_VALUE(v_body, ''$.provider''),
|
||||
JSON_VALUE(v_body, ''$.model_name''),
|
||||
JSON_VALUE(v_body, ''$.operation''),
|
||||
TO_NUMBER(JSON_VALUE(v_body, ''$.project_id'')),
|
||||
TO_NUMBER(JSON_VALUE(v_body, ''$.catalog_id'')),
|
||||
NVL(TO_NUMBER(JSON_VALUE(v_body, ''$.prompt_tokens'')), 0),
|
||||
NVL(TO_NUMBER(JSON_VALUE(v_body, ''$.completion_tokens'')), 0),
|
||||
NVL(TO_NUMBER(JSON_VALUE(v_body, ''$.total_tokens'')), 0),
|
||||
NVL(TO_NUMBER(JSON_VALUE(v_body, ''$.cost_eur'')), 0)
|
||||
)
|
||||
RETURNING id INTO v_new_id;
|
||||
|
||||
:status_code := 201;
|
||||
HTP.P(''{"id":'' || v_new_id || ''}'');
|
||||
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
ROLLBACK;
|
||||
:status_code := 500;
|
||||
HTP.P(''{"error":"'' || REPLACE(SQLERRM, ''"'', '''''''') || ''"}'' );
|
||||
END;
|
||||
'
|
||||
);
|
||||
|
||||
-- GET /api/dc/ai-costs
|
||||
ORDS.DEFINE_HANDLER(
|
||||
p_module_name => 'frigosped.dc',
|
||||
p_pattern => 'ai-costs',
|
||||
p_method => 'GET',
|
||||
p_source_type => ORDS.SOURCE_TYPE_PLSQL,
|
||||
p_comments => 'Kostenauswertung: total, current_month, monatliche Aufschluesselung via DC_COSTS_PKG',
|
||||
p_source => '
|
||||
DECLARE
|
||||
v_total_eur NUMBER;
|
||||
v_total_tokens NUMBER;
|
||||
v_total_calls NUMBER;
|
||||
v_month_eur NUMBER;
|
||||
v_month_tokens NUMBER;
|
||||
v_month_calls NUMBER;
|
||||
v_monthly_json CLOB;
|
||||
BEGIN
|
||||
DC_COSTS_PKG.get_summary(
|
||||
v_total_eur, v_total_tokens, v_total_calls,
|
||||
v_month_eur, v_month_tokens, v_month_calls
|
||||
);
|
||||
|
||||
SELECT JSON_ARRAYAGG(
|
||||
JSON_OBJECT(
|
||||
''month'' VALUE month,
|
||||
''cost_eur'' VALUE ROUND(cost_eur, 6),
|
||||
''prompt_tokens'' VALUE prompt_tokens,
|
||||
''completion_tokens'' VALUE completion_tokens,
|
||||
''total_tokens'' VALUE total_tokens,
|
||||
''call_count'' VALUE call_count
|
||||
)
|
||||
ORDER BY month DESC
|
||||
RETURNING CLOB
|
||||
)
|
||||
INTO v_monthly_json
|
||||
FROM (
|
||||
SELECT
|
||||
TO_CHAR(called_at, ''YYYY-MM'') AS month,
|
||||
SUM(cost_eur) AS cost_eur,
|
||||
SUM(prompt_tokens) AS prompt_tokens,
|
||||
SUM(completion_tokens) AS completion_tokens,
|
||||
SUM(total_tokens) AS total_tokens,
|
||||
COUNT(*) AS call_count
|
||||
FROM dc_ai_cost_log
|
||||
WHERE called_at >= ADD_MONTHS(TRUNC(SYSDATE, ''MM''), -11)
|
||||
GROUP BY TO_CHAR(called_at, ''YYYY-MM'')
|
||||
);
|
||||
|
||||
:status_code := 200;
|
||||
HTP.P(
|
||||
''{"total":{''
|
||||
|| '' "cost_eur":'' || ROUND(NVL(v_total_eur, 0), 6)
|
||||
|| '', "total_tokens":'' || NVL(v_total_tokens, 0)
|
||||
|| '', "call_count":'' || NVL(v_total_calls, 0)
|
||||
|| ''}, "current_month":{''
|
||||
|| '' "month":"'' || TO_CHAR(SYSDATE, ''YYYY-MM'') || ''"''
|
||||
|| '', "cost_eur":'' || ROUND(NVL(v_month_eur, 0), 6)
|
||||
|| '', "total_tokens":'' || NVL(v_month_tokens, 0)
|
||||
|| '', "call_count":'' || NVL(v_month_calls, 0)
|
||||
|| ''}, "monthly":'' || NVL(v_monthly_json, ''[]'')
|
||||
|| ''}''
|
||||
);
|
||||
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
:status_code := 500;
|
||||
HTP.P(''{"error":"'' || REPLACE(SQLERRM, ''"'', '''''''') || ''"}'' );
|
||||
END;
|
||||
'
|
||||
);
|
||||
|
||||
COMMIT;
|
||||
END;
|
||||
/
|
||||
Reference in New Issue
Block a user