Files
vpd-permission-poc/sql/adb/59_kb_select_ai_router_ords.sql
2026-07-08 06:43:29 +09:00

144 lines
4.6 KiB
SQL

-- ============================================================
-- 59_kb_select_ai_router_ords.sql
--
-- POST /ords/cb-ords/kb-select-ai-agent/run
-- Authorization: Bearer <VPD token>
-- {"prompt":"...","conversationId":"optional-safe-id"}
--
-- The ORDS runtime validates the bearer and sets the application context
-- before invoking the POC_2-owned Team API. The context is session scoped,
-- so VPD still applies to any SQL action selected by the Select AI tool.
--
-- Execute as CB_ORDS after 22 and 58.
-- ============================================================
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET ECHO OFF
SET FEEDBACK ON
SET DEFINE OFF
PROMPT === Resetting only the KB Select AI Agent ORDS module ===
BEGIN
ORDS.DELETE_MODULE(p_module_name => 'kb.select.ai.agent');
EXCEPTION
WHEN OTHERS THEN NULL;
END;
/
PROMPT === Creating KB Select AI Agent ORDS endpoint ===
BEGIN
ORDS.DEFINE_MODULE(
p_module_name => 'kb.select.ai.agent',
p_base_path => 'kb-select-ai-agent/',
p_items_per_page => 0,
p_status => 'PUBLISHED'
);
ORDS.DEFINE_TEMPLATE(
p_module_name => 'kb.select.ai.agent',
p_pattern => 'run'
);
ORDS.DEFINE_HANDLER(
p_module_name => 'kb.select.ai.agent',
p_pattern => 'run',
p_method => 'POST',
p_source_type => ORDS.source_type_plsql,
p_source => q'~
DECLARE
v_body_text CLOB;
v_prompt VARCHAR2(32767);
v_requested_conversation_id VARCHAR2(128);
v_internal_conversation_id VARCHAR2(128);
v_stakeholder_user_id VARCHAR2(4000);
v_answer CLOB;
BEGIN
cb_ords_handler_pkg.set_vpd_context(:auth_header, :probe_id);
-- ORDS exposes request JSON as a stream bind. Read it once only.
v_body_text := :body_text;
v_prompt := JSON_VALUE(v_body_text, '$.prompt' RETURNING VARCHAR2(32767));
v_requested_conversation_id := JSON_VALUE(v_body_text, '$.conversationId' RETURNING VARCHAR2(128));
IF v_prompt IS NULL OR TRIM(v_prompt) IS NULL THEN
RAISE_APPLICATION_ERROR(-20801, 'prompt is required');
END IF;
IF v_requested_conversation_id IS NULL THEN
v_requested_conversation_id := 'kb-' || LOWER(RAWTOHEX(SYS_GUID()));
ELSIF NOT REGEXP_LIKE(v_requested_conversation_id, '^[A-Za-z0-9._:-]{1,128}$') THEN
RAISE_APPLICATION_ERROR(-20802, 'conversationId contains unsupported characters');
END IF;
-- A connection-pooled ORDS session can be reused. Scope the Team's
-- conversation memory to the VPD subject as well as the caller's public ID.
v_stakeholder_user_id := NVL(SYS_CONTEXT('CB_AGENT_CTX', 'STAKEHOLDER_USER_ID'), 'anonymous');
v_internal_conversation_id := 'kb-' || LOWER(RAWTOHEX(STANDARD_HASH(
v_stakeholder_user_id || ':' || v_requested_conversation_id,
'SHA256'
)));
v_answer := poc_2.kb_select_ai_router_api.run_team(
p_prompt => v_prompt,
p_conversation_id => v_internal_conversation_id
);
:status_code := 200;
OWA_UTIL.MIME_HEADER('application/json', FALSE);
HTP.P('Cache-Control: no-store');
OWA_UTIL.HTTP_HEADER_CLOSE;
APEX_JSON.OPEN_OBJECT;
APEX_JSON.WRITE('team', 'KB_SELECT_AI_ROUTER_TEAM');
APEX_JSON.WRITE('conversationId', v_requested_conversation_id);
APEX_JSON.WRITE('answer', v_answer);
APEX_JSON.CLOSE_OBJECT;
cb_ords_handler_pkg.clear_vpd_context;
EXCEPTION
WHEN OTHERS THEN
cb_ords_handler_pkg.clear_vpd_context;
:status_code := CASE
WHEN SQLCODE IN (-20801, -20802) OR SQLCODE BETWEEN -40599 AND -40400 THEN 400
WHEN SQLCODE BETWEEN -20199 AND -20100 THEN 403
ELSE 500
END;
OWA_UTIL.MIME_HEADER('application/json', FALSE);
HTP.P('Cache-Control: no-store');
OWA_UTIL.HTTP_HEADER_CLOSE;
APEX_JSON.OPEN_OBJECT;
APEX_JSON.WRITE('errorCode', SQLCODE);
APEX_JSON.WRITE('error', SQLERRM);
APEX_JSON.CLOSE_OBJECT;
END;
~',
p_items_per_page => 0
);
ORDS.DEFINE_PARAMETER(
p_module_name => 'kb.select.ai.agent',
p_pattern => 'run',
p_method => 'POST',
p_name => 'Authorization',
p_bind_variable_name => 'auth_header',
p_source_type => 'HEADER',
p_param_type => 'STRING',
p_access_method => 'IN'
);
ORDS.DEFINE_PARAMETER(
p_module_name => 'kb.select.ai.agent',
p_pattern => 'run',
p_method => 'POST',
p_name => 'X-VPD-Probe-Id',
p_bind_variable_name => 'probe_id',
p_source_type => 'HEADER',
p_param_type => 'STRING',
p_access_method => 'IN'
);
COMMIT;
END;
/
PROMPT === KB Select AI Agent ORDS endpoint ready ===
PROMPT Path: /ords/cb-ords/kb-select-ai-agent/run
EXIT