From 165b1049a9384f70f8dc75d841a7ccbf4b357b91 Mon Sep 17 00:00:00 2001 From: "Kona (Koshi Nakamura)" Date: Thu, 2 Apr 2026 13:46:33 +0900 Subject: [PATCH 1/2] Restructure ROI reporting agent for tdx agent push compatibility Restructure agent directory to match tdx agent pull/push file conventions so the entire agent (KB, tools, outputs, form interfaces) can be deployed with a single `tdx agent push .` command. Key changes: - Add TD-Managed_ Dashboard Viz/agent.yml with tools (LIST_COLUMNS, QUERY_DATA_DIRECT, READ_TEXT enums) and outputs (with json_schema) - Add TD-Managed_ Dashboard Viz/prompt.md (system prompt) - Add knowledge_bases/ with database KB and text KBs (with frontmatter) - Convert form_interfaces to use form_schema (YAML object) instead of form_json_schema (JSON string), and ui_schema instead of form_ui_schema - Remove old agent.yml and tools.yml (replaced by new structure) - Update README with CLI push instructions and OAuth vs API key warning - Add email_domains filtering note for non-Engage delivery databases Co-Authored-By: Claude Opus 4.6 (1M context) --- engage-box/roi_reporting/agent/README.md | 33 ++-- .../agent/TD-Managed_ Dashboard Viz/agent.yml | 66 +++++++ .../agent/TD-Managed_ Dashboard Viz/prompt.md | 146 +++++++++++++++ engage-box/roi_reporting/agent/agent.yml | 17 -- .../td_managed_campaign_details.yml | 51 +++++- .../td_managed_overall_summary.yml | 46 ++++- .../knowledge_bases/CampaignDetails_Spec.md | 170 ++++++++++++++++++ .../knowledge_bases/OverallSummary_Spec.md | 144 +++++++++++++++ .../knowledge_bases/engage_roi_reporting.yml | 19 ++ engage-box/roi_reporting/agent/tools.yml | 97 ---------- .../workflows/reporting_agent/README.md | 8 +- 11 files changed, 663 insertions(+), 134 deletions(-) create mode 100644 engage-box/roi_reporting/agent/TD-Managed_ Dashboard Viz/agent.yml create mode 100644 engage-box/roi_reporting/agent/TD-Managed_ Dashboard Viz/prompt.md delete mode 100644 engage-box/roi_reporting/agent/agent.yml create mode 100644 engage-box/roi_reporting/agent/knowledge_bases/CampaignDetails_Spec.md create mode 100644 engage-box/roi_reporting/agent/knowledge_bases/OverallSummary_Spec.md create mode 100644 engage-box/roi_reporting/agent/knowledge_bases/engage_roi_reporting.yml delete mode 100644 engage-box/roi_reporting/agent/tools.yml diff --git a/engage-box/roi_reporting/agent/README.md b/engage-box/roi_reporting/agent/README.md index b3673c5d..f7e37386 100644 --- a/engage-box/roi_reporting/agent/README.md +++ b/engage-box/roi_reporting/agent/README.md @@ -23,12 +23,17 @@ Two report types are supported: | File | Description | |---|---| -| `system_prompt.md` | Agent system prompt — paste into System Prompt field | -| `knowledge_base_overall_summary.md` | Report spec for Overall Summary — register as Text KB named `OverallSummary_Spec` | -| `knowledge_base_campaign_details.md` | Report spec for Campaign Details — register as Text KB named `CampaignDetails_Spec` | -| `tools.yml` | All tool configurations — reference when configuring agent tools | -| `forms/td_managed_overall_summary.yml` | Form interface for Overall Summary report | -| `forms/td_managed_campaign_details.yml` | Form interface for Campaign Details report | +| `TD-Managed_ Dashboard Viz/agent.yml` | Agent configuration with tools and outputs | +| `TD-Managed_ Dashboard Viz/prompt.md` | Agent system prompt | +| `knowledge_bases/engage_roi_reporting.yml` | Database KB definition | +| `knowledge_bases/OverallSummary_Spec.md` | Text KB — Overall Summary report spec | +| `knowledge_bases/CampaignDetails_Spec.md` | Text KB — Campaign Details report spec | +| `form_interfaces/td_managed_overall_summary.yml` | Form interface for Overall Summary report | +| `form_interfaces/td_managed_campaign_details.yml` | Form interface for Campaign Details report | +| `system_prompt.md` | System prompt source (English) | +| `system_prompt_JA.md` | System prompt source (Japanese) | +| `knowledge_base_overall_summary.md` | Report spec source (English) | +| `knowledge_base_campaign_details.md` | Report spec source (English) | ## Prerequisites @@ -67,12 +72,15 @@ Two report types are supported: ```bash # 1. Clone or download this directory -# 2. Edit tools.yml and replace with your actual database name -# 3. Run: +# 2. Create the LLM project tdx llm project create "ROI Reporting Agent" +# 3. Push from the agent/ directory (project root) +cd agent/ tdx agent push . -f ``` +> **Note**: You must push from the project root directory (where `tdx.json` is located) to include knowledge bases and form interfaces. Pushing from an agent subdirectory only syncs that agent's configuration. + ### Option B: Manual (AI Agent Foundry UI) #### 1. Create Project @@ -100,11 +108,12 @@ Create a new project named **`ROI Reporting Agent`** in AI Agent Foundry. - Temperature: 0 #### 5. Configure Tools -See **[tools.yml](./tools.yml)** for all tool names, descriptions, and settings. +See **[TD-Managed_ Dashboard Viz/agent.yml](./TD-Managed_%20Dashboard%20Viz/agent.yml)** for all tool names, descriptions, and settings. -#### 6. Register Form Interfaces (when API available) -- Overall Summary: `forms/td_managed_overall_summary.yml` -- Campaign Details: `forms/td_managed_campaign_details.yml` +#### 6. Register Form Interfaces +Form interfaces are automatically deployed via `tdx agent push` when pushing from the project root. +- Overall Summary: `form_interfaces/td_managed_overall_summary.yml` +- Campaign Details: `form_interfaces/td_managed_campaign_details.yml` ## Usage diff --git a/engage-box/roi_reporting/agent/TD-Managed_ Dashboard Viz/agent.yml b/engage-box/roi_reporting/agent/TD-Managed_ Dashboard Viz/agent.yml new file mode 100644 index 00000000..72cf0d63 --- /dev/null +++ b/engage-box/roi_reporting/agent/TD-Managed_ Dashboard Viz/agent.yml @@ -0,0 +1,66 @@ +name: "TD-Managed: Dashboard Viz" +model: claude-4.5-sonnet +temperature: 0 +max_tool_iterations: 4 + +tools: + - type: knowledge_base + target: '@ref(type: "knowledge_base", name: "engage_roi_reporting")' + target_function: LIST_COLUMNS + function_name: list_columns + function_description: > + Get data schema. Discover table schemas and return column names, types, + and comments for tables in the ROI reporting database. + + - type: knowledge_base + target: '@ref(type: "knowledge_base", name: "engage_roi_reporting")' + target_function: QUERY_DATA_DIRECT + function_name: query_data + function_description: > + Run Presto/Trino query against the ROI reporting database. + Max 100 rows returned. Use GROUP BY aggregations. Never SELECT *. + If result contains [TRUNCATED], use OFFSET and LIMIT for pagination. + + - type: knowledge_base + target: '@ref(type: "knowledge_base", name: "OverallSummary_Spec")' + target_function: READ_TEXT + function_name: Read_OverallSummary_Spec + function_description: > + Spec for OverallSummary report. Read the Overall Summary report + specification from knowledge base. + + - type: knowledge_base + target: '@ref(type: "knowledge_base", name: "CampaignDetails_Spec")' + target_function: READ_TEXT + function_name: Read_CampaignDetails_Spec + function_description: > + Spec for Individual Campaign Detail Report. Read the Campaign/Journey + Detail report specification from knowledge base. + +outputs: + - name: renderReactApp + function_name: renderReactApp + function_description: > + Generates React components using Tailwind CSS. Use ONLY when explicitly + requested (React, PDF, Dashboard). STRICT: Single file, export default. + NO external libs (no lucide-react). Use inline SVGs. Self-contained. + Render visual content ONLY; NO print/download buttons (system handled). + json_schema: '{"type":"object","properties":{"code":{"type":"string","description":"React JSX code"}},"required":["code"]}' + + - name: text_in_form + function_name: text_in_form + function_description: > + Out text with MD format. Use this when you want to output an error message. + json_schema: '{"type":"object","properties":{"text":{"type":"string","description":"Markdown formatted text"}},"required":["text"]}' + + - name: ":plotly:" + function_name: new_plot + function_description: > + Provide visualization for analysis result by rendering charts using Plotly.js. + Use the color scheme: ["B4E3E3", "ABB3DB", "D9BFDF", "F8E1B0", "8FD6D4", + "828DCA", "C69ED0", "F5D389", "6AC8C6", "5867B8", "B37EC0", "F1C461", + "44BAB8", "2E41A6", "8CC97E", "A05EB0"]. + For charts with more than three categories, actively use updatemenus. + When summarizing multiple analysis, combine relevant charts into a single + dashboard using Plotly grid layout. Prevent text overlap with adequate margins. + json_schema: '{"type":"object","properties":{"data":{"type":"array","description":"Plotly.js data JSON objects","items":{"type":"object"}},"layout":{"type":"object","description":"Plotly.js layout JSON object"}},"required":["data"]}' diff --git a/engage-box/roi_reporting/agent/TD-Managed_ Dashboard Viz/prompt.md b/engage-box/roi_reporting/agent/TD-Managed_ Dashboard Viz/prompt.md new file mode 100644 index 00000000..b483ef66 --- /dev/null +++ b/engage-box/roi_reporting/agent/TD-Managed_ Dashboard Viz/prompt.md @@ -0,0 +1,146 @@ +## Your Role + +You are a unified ROI reporting agent that autonomously generates dashboard reports. Based on specifications and user instructions, you execute SQL queries against Trino, visualize intermediate results, and generate a single self-contained .jsx file. + +## Core Principles + +- Graceful Degradation: Deliver the best possible report using available data. If components fail, continue with successful ones. +- Autonomous Execution: Execute end-to-end without waiting for user prompts until final renderReactApp call. Never ask for optional parameters - proceed with available data. +- Progressive Disclosure: Show intermediate Plotly visualizations for each component. +- Silent Final Assembly: Last action must be a single renderReactApp call with complete code. + +## Execution Flow + +1. Planning + - Read YAML spec, list all components, create build plan + - Note: Summary executed LAST but rendered FIRST in output + - Evaluate component-level display_condition, exclude if false + - Normalize inputs: report_spec_name, component_id, filters + +2. Data Retrieval (Per Component) + - Schema Validation: Verify all required columns exist + - SQL Generation: Follow YAML spec and Trino SQL Cookbook below + - Apply filters strictly (no fuzzy matching) + - Execute query, retry on failure with corrections + - If zero rows: record error, skip component + - Show intermediate visualization via render_plotly_chart + +3. Summary Generation + - Execute AFTER all components (step 3), render FIRST in output (step 4) + - Provide data-driven insights using ONLY retrieved SQL results + - Describe what data shows, NOT what you generated + - Mention missing components/metrics if applicable + - Constraints: No calculations, no new queries, no assumptions + +4. Final Build + - Render order: Title → Summary → Components (spec order) + - Evaluate metric-level display_condition at render time + - Generate React components per component_type + - Call renderReactApp once with complete code + +## Display Condition Rules + +- Component-level: Evaluated before SQL (planning). If false, skip entire component. +- Metric-level: Evaluated after SQL (rendering). If false, hide only that metric within component. +- Never hide entire component due to metric-level condition. + +## Trino SQL Cookbook + +- Division: CAST(SUM(num) AS DOUBLE) / NULLIF(SUM(denom), 0) +- Conditional Aggregation: SUM(CASE WHEN cond THEN 1 ELSE 0 END) +- Date varchar: WHERE col BETWEEN '...' AND '...'. For functions: CAST(col AS DATE) +- Timestamp varchar: date_parse(col, '%Y-%m-%d %H:%i:%s.%f') +- Time Series Zero-Filling: + WITH date_range AS (SELECT CAST(MIN(...) AS DATE) AS s, CAST(MAX(...) AS DATE) AS e FROM ...), + time_series AS (SELECT t.dt FROM date_range CROSS JOIN UNNEST(SEQUENCE(s, e, INTERVAL '1' DAY)) AS t(dt)) + SELECT ... FROM time_series LEFT JOIN ... +- Ranking: Use WITH clauses, no ROW_NUMBER() +- Final SELECT: No GROUP BY or aggregates in final SELECT +- Ordering: Use spec's orderby_clause_template if available +- LIMIT: Follow spec's notes exactly + +## Filter Rules + +- Strict matching only (no modification, no fuzzy matching) +- Optionally verify filters yield >0 rows +- Required filters (required: true in spec): Must be provided or call text_in_form +- Optional filters: If not provided, skip components that require them (use display_condition) +- NEVER ask user for optional filter values - proceed with available data only + +## Error Handling + +- Missing arguments: Call text_in_form, stop +- Missing OPTIONAL arguments: Proceed without them (skip related components if needed) **Important:** Required arguments are explicitly marked as "required: true" in spec filters. All other arguments are optional and should NOT trigger user prompts. +- Schema mismatch: Record error, continue to next component +- SQL failure: Analyze, retry. If unresolved, record error, continue +- Zero data: Record {code: "NO_DATA_FOR_FILTER"}, skip component +- No successful components: Call text_in_form + +## Intermediate Visualization + +- After each successful data retrieval +- Use render_plotly_chart: bar for KPIs, table for tables, line for trends +- Include component title and brief status + +## Final Build: React Generation + +- Single .jsx file, no relative imports +- Imports: import React from 'react'; import Plot from 'react-plotly.js'; +- Prohibited: @mui/material, styled-components, Plotly.newPlot() +- React Hooks: React.useState(), React.useEffect(), React.useRef() +- Plotly: Use component, color scheme: ["#B4E3E3", "#ABB3DB", "#D9BFDF", "#F8E1B0", "#8FD6D4", "#828DCA", "#C69ED0", "#F5D389", "#6AC8C6", "#5867B8", "#B37EC0", "#F1C461", "#44BAB8", "#2E41A6", "#8CC97E", "#A05EB0"] +- For >3 categories: use updatemenus. For multi-chart: grid layout +- Margins: {l: 80, r: 80, t: 100, b: 80}, min dimensions: height 600, width 1000 +- Ensure the main component container's boxShadow style is set to none to eliminate external borders or shadows. + +## Formatting Rules + +- percentage: "25.5%" (1 decimal), "0%" if exactly 0, "N/A" if null +- currency: "¥1,234.5" (1 decimal, thousands separator), "¥0" if exactly 0, "N/A" if null +- integer: "1,234" (no decimal, thousands separator), "N/A" if null +- All nulls display as "N/A" (not blank, dash, or "null") + +## Component Rendering Patterns + +KPI Cards (kpi_card_group): +- Group related metrics in single cards (e.g., Opens + Open Rate) +- Primary metric: larger font (20-24px), bold +- Secondary metrics: smaller font (14-16px) +- Each card: border, boxShadow, padding (20px), margin (10px) +- Responsive grid layout + +Tables (table): +- Each metric in separate column (never combine in single cell) +- Text columns: left-aligned; Number columns: right-aligned +- Borders, alternating row colors, bold headers + +Line Charts (line_chart): +- Use with mode: 'lines+markers' +- Single y-axis for all metrics +- Time series on x-axis + +Dual-Axis Line Charts (dual_axis_line_chart): +- Use with two y-axes +- Left axis (yaxis): Metrics with axis: "left" +- Right axis (yaxis2): Metrics with axis: "right" +- Layout configuration: + layout = { + yaxis: {title: 'Left Axis Title', side: 'left'}, + yaxis2: {title: 'Right Axis Title', side: 'right', overlaying: 'y'} + } +- Assign traces: yaxis: 'y' (left) or yaxis: 'y2' (right) +- If all right-axis metrics hidden by display_condition, use single axis only + +## Design Principles + +- Consistent fonts, colors, margins, padding +- Sufficient contrast, font sizes: titles ~18px, body ~14px +- Wrap components in
with border, boxShadow, padding +- 20-30px margins between components + +## Constraints + +- No intermediate natural language output (only tool calls and brief progress) +- No translation/rounding during query (apply formatting in JSX only) +- Validate component existence, row/column consistency +- Errors and successful data may coexist (record errors, continue) diff --git a/engage-box/roi_reporting/agent/agent.yml b/engage-box/roi_reporting/agent/agent.yml deleted file mode 100644 index 1a4fe6ac..00000000 --- a/engage-box/roi_reporting/agent/agent.yml +++ /dev/null @@ -1,17 +0,0 @@ -llm_project: "ROI Reporting Agent" -name: "TD-Managed: Dashboard Viz" -model: claude-4.5-sonnet -max_tool_iterations: 4 -temperature: 0 -system_prompt_file: system_prompt.md -knowledge_bases: - - name: OverallSummary_Spec - type: text - file: knowledge_base_overall_summary.md - - name: CampaignDetails_Spec - type: text - file: knowledge_base_campaign_details.md - - name: engage_roi_reporting - type: database - database: engage_roi_reporting -tools_file: tools.yml diff --git a/engage-box/roi_reporting/agent/form_interfaces/td_managed_campaign_details.yml b/engage-box/roi_reporting/agent/form_interfaces/td_managed_campaign_details.yml index 71348f24..fc4dffe2 100644 --- a/engage-box/roi_reporting/agent/form_interfaces/td_managed_campaign_details.yml +++ b/engage-box/roi_reporting/agent/form_interfaces/td_managed_campaign_details.yml @@ -1,5 +1,5 @@ name: "TD-Managed: Campaign Details" -agent: "@ref(type: \"agent\", name: \"TD-Managed: Dashboard Viz\")" +agent: '@ref(type: "agent", name: "TD-Managed: Dashboard Viz")' prompt_template: | Create dashboard with following conditions: - Report_id: 2. Campaign Summary @@ -7,6 +7,51 @@ prompt_template: | - Journey_id: {{Journey_id}}, - Language: {{Language}}, - Currency: {{Currency}} -form_json_schema: '{"type":"object","properties":{"Campaign_id":{"title":"Campaign ID","type":"string","placeholder":"Enter either \"Campaign ID\" or \"Journey ID\""},"Journey_id":{"title":"Journey ID","type":"string","placeholder":"Enter either \"Campaign ID\" or \"Journey ID\""},"Language":{"title":"Language","type":"string","enum":["English","Japanese"],"enumNames":["English","Japanese"]},"Currency":{"title":"Currency","type":"string","enum":["USD","JPY"],"enumNames":["USD","JPY"]}},"required":["Language","Currency"]}' -form_ui_schema: '{"Campaign_id":{"ui:en:title":"Campaign ID","ui:en:placeholder":"Enter either \"Campaign ID\" or \"Journey ID\"","ui:ja:placeholder":"キャンペーンIDまたは、ジャーニーIDを入力してください","ui:ja:title":"キャンペーンID"},"Journey_id":{"ui:en:title":"Journey ID","ui:en:placeholder":"Enter either \"Campaign ID\" or \"Journey ID\"","ui:ja:title":"ジャーニーID","ui:ja:placeholder":"キャンペーンIDまたは、ジャーニーIDを入力してください"},"Language":{"ui:widget":"radio","ui:en:title":"Language","ui:ja:title":"言語","ui:enumNames":["English","Japanese"],"ui:en:enumNames":["English","Japanese"],"ui:ja:enumNames":["英語","日本語"]},"Currency":{"ui:widget":"radio","ui:en:title":"Currency","ui:ja:title":"通貨","ui:enumNames":["USD","JPY"],"ui:en:enumNames":["USD","JPY"],"ui:ja:enumNames":["米ドル","円"]}}' +form_schema: + type: object + properties: + Campaign_id: + title: Campaign ID + type: string + placeholder: 'Enter either "Campaign ID" or "Journey ID"' + Journey_id: + title: Journey ID + type: string + placeholder: 'Enter either "Campaign ID" or "Journey ID"' + Language: + title: Language + type: string + enum: ["English", "Japanese"] + enumNames: ["English", "Japanese"] + Currency: + title: Currency + type: string + enum: ["USD", "JPY"] + enumNames: ["USD", "JPY"] + required: ["Language", "Currency"] +ui_schema: + Campaign_id: + "ui:en:title": Campaign ID + "ui:en:placeholder": 'Enter either "Campaign ID" or "Journey ID"' + "ui:ja:placeholder": キャンペーンIDまたは、ジャーニーIDを入力してください + "ui:ja:title": キャンペーンID + Journey_id: + "ui:en:title": Journey ID + "ui:en:placeholder": 'Enter either "Campaign ID" or "Journey ID"' + "ui:ja:title": ジャーニーID + "ui:ja:placeholder": キャンペーンIDまたは、ジャーニーIDを入力してください + Language: + "ui:widget": radio + "ui:en:title": Language + "ui:ja:title": 言語 + "ui:enumNames": ["English", "Japanese"] + "ui:en:enumNames": ["English", "Japanese"] + "ui:ja:enumNames": ["英語", "日本語"] + Currency: + "ui:widget": radio + "ui:en:title": Currency + "ui:ja:title": 通貨 + "ui:enumNames": ["USD", "JPY"] + "ui:en:enumNames": ["USD", "JPY"] + "ui:ja:enumNames": ["米ドル", "円"] use_text_resource: false diff --git a/engage-box/roi_reporting/agent/form_interfaces/td_managed_overall_summary.yml b/engage-box/roi_reporting/agent/form_interfaces/td_managed_overall_summary.yml index 8da06d12..3cbe5122 100644 --- a/engage-box/roi_reporting/agent/form_interfaces/td_managed_overall_summary.yml +++ b/engage-box/roi_reporting/agent/form_interfaces/td_managed_overall_summary.yml @@ -1,5 +1,5 @@ name: "TD-Managed: Overall Summary" -agent: "@ref(type: \"agent\", name: \"TD-Managed: Dashboard Viz\")" +agent: '@ref(type: "agent", name: "TD-Managed: Dashboard Viz")' prompt_template: | Create dashboard with following conditions: - Report_id: 1.Overall Summary @@ -8,6 +8,46 @@ prompt_template: | - Timezone: {{Timezone}}, - Language: {{Language}}, - Currency: {{Currency}} -form_json_schema: '{"type":"object","properties":{"Start_date":{"title":"Start Date","type":"string","format":"date"},"End_date":{"title":"End Date","type":"string","format":"date"},"Language":{"title":"Language","type":"string","enum":["English","Japanese"],"enumNames":["English","Japanese"]},"Currency":{"title":"Currency","type":"string","enum":["USD","JPY"],"readOnly":false,"enumNames":["USD","JPY"]}},"required":["Start_date","Language","End_date","Currency"]}' -form_ui_schema: '{"Start_date":{"ui:en:title":"Start Date"},"End_date":{"ui:en:title":"End Date"},"Language":{"ui:widget":"radio","ui:en:title":"Language","ui:enumNames":["English","Japanese"],"ui:en:enumNames":["English","Japanese"],"ui:ja:enumNames":["英語","日本語"],"ui:ja:title":"言語"},"Currency":{"ui:widget":"radio","ui:en:title":"Currency","ui:ja:title":"通貨","ui:enumNames":["USD","JPY"],"ui:en:enumNames":["USD","JPY"],"ui:ja:enumNames":["米ドル","円"]}}' +form_schema: + type: object + properties: + Start_date: + title: Start Date + type: string + format: date + End_date: + title: End Date + type: string + format: date + Language: + title: Language + type: string + enum: ["English", "Japanese"] + enumNames: ["English", "Japanese"] + Currency: + title: Currency + type: string + enum: ["USD", "JPY"] + readOnly: false + enumNames: ["USD", "JPY"] + required: ["Start_date", "Language", "End_date", "Currency"] +ui_schema: + Start_date: + "ui:en:title": Start Date + End_date: + "ui:en:title": End Date + Language: + "ui:widget": radio + "ui:en:title": Language + "ui:enumNames": ["English", "Japanese"] + "ui:en:enumNames": ["English", "Japanese"] + "ui:ja:enumNames": ["英語", "日本語"] + "ui:ja:title": 言語 + Currency: + "ui:widget": radio + "ui:en:title": Currency + "ui:ja:title": 通貨 + "ui:enumNames": ["USD", "JPY"] + "ui:en:enumNames": ["USD", "JPY"] + "ui:ja:enumNames": ["米ドル", "円"] use_text_resource: false diff --git a/engage-box/roi_reporting/agent/knowledge_bases/CampaignDetails_Spec.md b/engage-box/roi_reporting/agent/knowledge_bases/CampaignDetails_Spec.md new file mode 100644 index 00000000..bd139fe5 --- /dev/null +++ b/engage-box/roi_reporting/agent/knowledge_bases/CampaignDetails_Spec.md @@ -0,0 +1,170 @@ +--- +name: CampaignDetails_Spec +--- + +# Report Specification: Campaign Details + +## 1. Report Overview +- purpose: "To provide comprehensive performance analysis for a single campaign or journey, including KPIs, trends, and email title breakdown." +- source_tables: + - "email_events" + - "revenue" + +## 2. Filters +- filter: + - id: "campaign_id" + type: "string" + required: true + exclusive_with: "journey_id" +- filter: + - id: "journey_id" + type: "string" + required: true + exclusive_with: "campaign_id" +- filter_notes: "A campaign_id or journey_id is required. When one of these IDs is provided, a date range is NOT required. The report should run on all available data for that ID." +- notes: "Timestamp columns (event_timestamp, conversion_timestamp) are varchar strings. They MUST be parsed using date_parse(column, '%Y-%m-%d %H:%i:%s.%f')." + +## 3. Important Notes on Metrics + +### Unique Count vs Total Count +- **Unique counts** (primary metrics): Use `COUNT(DISTINCT message_id)` to count each message only once, regardless of how many times it was opened/clicked. +- **Total counts** (supplementary information): Use `COUNT(*)` to count all events, including multiple opens/clicks of the same message. +- **Rate calculations**: All rates MUST use unique counts to avoid inflated metrics (industry standard). + +### Identifier Standard +- Use `message_id` (Amazon SES unique message ID) for DISTINCT aggregations. +- This ensures consistency with Email Delivery Reports and industry best practices. + +### Time Granularity (for trend components) + +**CRITICAL: Timestamp Parsing** +- event_timestamp and conversion_timestamp are VARCHAR strings, NOT TIMESTAMP types. +- You MUST use `date_parse(column, '%Y-%m-%d %H:%i:%s.%f')` to parse these columns. +- DO NOT use `CAST(column AS DATE)` or `CAST(column AS TIMESTAMP)` - these will fail. + +The SQL agent must dynamically set the time grain based on the total date range of available data for the specified campaign_id or journey_id: +- **<=20 days**: daily granularity +- **21-89 days**: weekly granularity (starting Monday) +- **>=90 days**: monthly granularity + +This ensures optimal visualization density and performance. + +## 4. Component Definitions + +### Component 1: Engagement KPIs +- component: + - component_id: "kpi_summary_engagement" + - component_type: "kpi_card_group" + - title: "Engagement KPIs for {name} ({id})" + - source_tables: ["email_events"] + - metrics: + - { metric_id: "sends", display_name: "Sends", calculation: "COUNT(*) FROM email_events WHERE event_type = 'Send'", format: "integer" } + - { metric_id: "deliveries", display_name: "Deliveries", calculation: "COUNT(*) FROM email_events WHERE event_type = 'Delivery'", format: "integer" } + - { metric_id: "unique_opens", display_name: "Unique Opens", calculation: "COUNT(DISTINCT message_id) FROM email_events WHERE event_type = 'Open'", format: "integer", visual_priority: "primary" } + - { metric_id: "total_opens", display_name: "Total Opens", calculation: "COUNT(*) FROM email_events WHERE event_type = 'Open'", format: "integer", visual_priority: "tertiary", display_note: "Supplementary information" } + - { metric_id: "unique_open_rate", display_name: "Unique Open Rate", calculation: "(COUNT(DISTINCT message_id) WHERE event_type = 'Open') / (COUNT(*) WHERE event_type = 'Delivery')", format: "percentage", visual_priority: "primary" } + - { metric_id: "unique_clicks", display_name: "Unique Clicks", calculation: "COUNT(DISTINCT message_id) FROM email_events WHERE event_type = 'Click'", format: "integer", visual_priority: "primary" } + - { metric_id: "total_clicks", display_name: "Total Clicks", calculation: "COUNT(*) FROM email_events WHERE event_type = 'Click'", format: "integer", visual_priority: "tertiary", display_note: "Supplementary information" } + - { metric_id: "unique_click_rate", display_name: "Unique Click Rate (CTR)", calculation: "(COUNT(DISTINCT message_id) WHERE event_type = 'Click') / (COUNT(*) WHERE event_type = 'Delivery')", format: "percentage", visual_priority: "primary" } + - { metric_id: "bounces", display_name: "Bounces", calculation: "COUNT(*) FROM email_events WHERE event_type = 'Bounce'", format: "integer" } + - { metric_id: "bounce_rate", display_name: "Bounce Rate", calculation: "(COUNT(*) WHERE event_type = 'Bounce') / (COUNT(*) WHERE event_type = 'Send')", format: "percentage" } + - { metric_id: "unsubscribes", display_name: "Unsubscribes", calculation: "COUNT(*) FROM email_events WHERE event_type = 'Complaint'", format: "integer" } + - notes: | + - Visual priority guidance: + * "primary": Display prominently (e.g., 32px bold for count, 24px bold for rate) + * "tertiary": Display as supplementary info (e.g., 13px regular, "Total Opens: {value}") + - All rate calculations MUST use unique counts (COUNT DISTINCT message_id) to avoid inflated rates. + +### Component 2: Revenue KPIs +- component: + - component_id: "kpi_summary_revenue" + - component_type: "kpi_card_group" + - title: "Revenue KPIs for {name} ({id})" + - source_tables: ["revenue", "email_events"] + - display_condition: "Only show this component if filtering by campaign_id." + - query_hints: + - "To get revenue for a journey, you MUST JOIN the 'revenue' and 'email_events' tables on 'campaign_id'." + - metrics: + - { metric_id: "total_revenue", display_name: "Total Revenue", calculation: "SUM(total_revenue) from revenue table where attribution_type is 'direct' or 'contributed'", format: "currency", display_condition: "Only show if both direct and contributed revenue exist." } + - { metric_id: "direct_revenue", display_name: "Direct Revenue", calculation: "SUM(total_revenue) from revenue table where attribution_type = 'direct'", format: "currency" } + - { metric_id: "contributed_revenue", display_name: "Contributed Revenue", calculation: "SUM(total_revenue) from revenue table where attribution_type = 'contributed'", format: "currency" } + +### Component 3: Performance by Email Title +- component: + - component_id: "email_title_performance" + - component_type: "table" + - title: "Performance by Email Title" + - source_tables: ["email_events"] + - dimensions: + - { id: "email_title", display_name: "Email Title" } + - metrics: + - { metric_id: "sends", display_name: "Sends", calculation: "COUNT(*) WHERE event_type = 'Send'", format: "integer" } + - { metric_id: "deliveries", display_name: "Deliveries", calculation: "COUNT(*) WHERE event_type = 'Delivery'", format: "integer" } + - { metric_id: "open_rate", display_name: "Open Rate", calculation: "(COUNT(*) WHERE event_type = 'Open') / (COUNT(*) WHERE event_type = 'Delivery')", format: "percentage" } + - { metric_id: "ctr", display_name: "CTR (Click-Through Rate)", calculation: "(COUNT(*) WHERE event_type = 'Click') / (COUNT(*) WHERE event_type = 'Delivery')", format: "percentage" } + - { metric_id: "bounces", display_name: "Bounces", calculation: "COUNT(*) WHERE event_type = 'Bounce'", format: "integer" } + - { metric_id: "bounce_rate", display_name: "Bounce Rate", calculation: "(COUNT(*) WHERE event_type = 'Bounce') / (COUNT(*) WHERE event_type = 'Send')", format: "percentage" } + - { metric_id: "unsubscribes", display_name: "Unsubscribes", calculation: "COUNT(*) WHERE event_type = 'Complaint'", format: "integer" } + - sort_order: "sends DESC, email_title ASC" + - notes: | + - Query Construction: + 1. GROUP BY email_title directly from email_events table + 2. Filter by campaign_id or journey_id + 3. No need for event_master table join + - SQL agent should query with a LIMIT of 51. + - If 51 rows are returned, the VIZ agent should display a note: 'Showing top 50 titles only. There may be additional titles not displayed.' + - Display the first 50 rows only. + +### Component 4: Engagement Count Trend +- component: + - component_id: "engagement_count_trend" + - component_type: "line_chart" + - title: "Engagement Trend (Counts)" + - source_tables: ["email_events"] + - y_axis_shared: true + - visualization_hint: "mode: 'lines+markers'" + - metrics: + - { metric_id: "sends", display_name: "Sends", calculation: "COUNT(CASE WHEN event_type='Send' THEN 1 END)", format: "integer" } + - { metric_id: "deliveries", display_name: "Deliveries", calculation: "COUNT(CASE WHEN event_type='Delivery' THEN 1 END)", format: "integer" } + - { metric_id: "unique_opens", display_name: "Unique Opens", calculation: "COUNT(DISTINCT CASE WHEN event_type='Open' THEN message_id END)", format: "integer", visual_priority: "primary" } + - { metric_id: "unique_clicks", display_name: "Unique Clicks", calculation: "COUNT(DISTINCT CASE WHEN event_type='Click' THEN message_id END)", format: "integer", visual_priority: "primary" } + - { metric_id: "bounce_count", display_name: "Bounces", calculation: "COUNT(CASE WHEN event_type='Bounce' THEN 1 END)", format: "integer" } + - { metric_id: "complaint_count", display_name: "Complaints", calculation: "COUNT(CASE WHEN event_type='Complaint' THEN 1 END)", format: "integer" } + - notes: | + - This component is based on the 'event_timestamp' column. + - Zero-padding is NOT required. + - Time granularity is determined by the total date range of the campaign/journey data (see Section 3). + - All metrics use COUNT DISTINCT message_id for opens and clicks to avoid inflated counts. + +### Component 5: Conversions and Revenue Trend +- component: + - component_id: "conversions_trend" + - component_type: "line_chart" + - title: "Conversions & Revenue Trend" + - source_tables: ["revenue", "email_events"] + - y_axis_shared: false + - visualization_hint: "mode: 'lines+markers', dual y-axis (left: count, right: currency)" + - display_condition: "Only show if filtering by campaign_id." + - metrics: + - { metric_id: "conversions", display_name: "Conversions", calculation: "COUNT(DISTINCT conversion_id) FROM revenue", format: "integer", y_axis: "left" } + - { metric_id: "direct_revenue", display_name: "Direct Revenue", calculation: "SUM(total_revenue) FROM revenue WHERE attribution_type = 'direct'", format: "currency", y_axis: "right" } + - { metric_id: "contributed_revenue", display_name: "Contributed Revenue", calculation: "SUM(total_revenue) FROM revenue WHERE attribution_type = 'contributed'", format: "currency", y_axis: "right" } + - { metric_id: "total_revenue", display_name: "Total Revenue", calculation: "SUM(total_revenue) FROM revenue WHERE attribution_type IN ('direct', 'contributed')", format: "currency", y_axis: "right" } + - notes: | + - This component is based on the 'conversion_timestamp' column. + - Zero-padding is NOT required. + - Time granularity is determined by the total date range of the campaign/journey data (see Section 3). + - Use dual y-axis: left for count metrics (conversions), right for currency metrics (revenue). + - This component helps identify day-of-week patterns and campaign timing effects. + +## 5. Component Rendering Order + +The final output should render components in the following order: + +1. Title +2. Summary (data-driven insights) +3. kpi_summary_engagement +4. kpi_summary_revenue (if campaign_id and data exists) +5. engagement_count_trend +6. conversions_trend (if campaign_id and data exists) +7. email_title_performance diff --git a/engage-box/roi_reporting/agent/knowledge_bases/OverallSummary_Spec.md b/engage-box/roi_reporting/agent/knowledge_bases/OverallSummary_Spec.md new file mode 100644 index 00000000..c0f74634 --- /dev/null +++ b/engage-box/roi_reporting/agent/knowledge_bases/OverallSummary_Spec.md @@ -0,0 +1,144 @@ +--- +name: OverallSummary_Spec +--- + +# Report Specification: OverallSummary + +## 1. Report Overview +- purpose: "To visualize key performance indicators (KPIs), trends, and top-performing campaigns/journeys over a specified period." +- source_tables: + - "daily_summary" + - "event_master" # Used for name lookups in rankings + +## 2. Filters +- filter: + - id: "date_range" + - type: "date" + - required: true + - notes: | + - A date range (start_date, end_date) is mandatory. + - If no range is provided, the SQL agent should return the min/max available dates and an error. + - The SQL agent will validate the range. If it exceeds 365 days, the agent will reject the request and return a message suggesting a valid 365-day range. + +## 3. Important Notes on Metrics + +**Note:** This section describes the calculation methodology. A user-facing disclaimer must be displayed at the end of the report (see Component: data_methodology_disclaimer). + +## 4. Component Definitions +- component: + - component_id: "kpi_summary" + - component_type: "kpi_card_group" + - title: "Overall Performance Summary" + - metrics: + - { metric_id: "sends", display_name: "Sends", calculation: "SUM(total_sends)", format: "integer" } + - { metric_id: "total_revenue", display_name: "Total Revenue", calculation: "SUM(total_revenue_direct + total_revenue_contributed)", format: "currency", display_condition: "Show only if SUM(total_revenue_direct) > 0 AND SUM(total_revenue_contributed) > 0" } + - { metric_id: "direct_revenue", display_name: "Direct Revenue", calculation: "SUM(total_revenue_direct)", format: "currency" } + - { metric_id: "contributed_revenue", display_name: "Contributed Revenue", calculation: "SUM(total_revenue_contributed)", format: "currency" } + - { metric_id: "conversions", display_name: "Conversions", calculation: "SUM(total_conversions)", format: "integer" } + - { metric_id: "deliveries", display_name: "Deliveries", calculation: "SUM(total_deliveries)", format: "integer" } + - { metric_id: "opens", display_name: "Opens", calculation: "SUM(total_opens)", format: "integer" } + - { metric_id: "open_rate", display_name: "Open Rate", calculation: "SUM(total_opens) / SUM(total_deliveries)", format: "percentage" } + - { metric_id: "clicks", display_name: "Clicks", calculation: "SUM(total_clicks)", format: "integer" } + - { metric_id: "click_rate", display_name: "Click Rate", calculation: "SUM(total_clicks) / SUM(total_deliveries)", format: "percentage" } + - { metric_id: "bounces", display_name: "Bounces", calculation: "SUM(total_hard_bounces + total_soft_bounces)", format: "integer" } + - { metric_id: "bounce_rate", display_name: "Bounce Rate", calculation: "SUM(total_hard_bounces + total_soft_bounces) / SUM(total_sends)", format: "percentage" } + - { metric_id: "unsubscribes", display_name: "Unsubscribes", calculation: "SUM(total_unsubscribes)", format: "integer" } + +- component: + - component_id: "campaign_performance_ranking" + - component_type: "table" + - title: "Top 5 Campaigns" + - source_tables: ["daily_summary", "event_master"] + - dimensions: + - { id: "campaign_name", display_name: "Campaign Name" } + - { id: "campaign_id", display_name: "Campaign ID" } + - metrics: + - { metric_id: "revenue", display_name: "Revenue", format: "currency" } + - { metric_id: "conversions", display_name: "Conversions", calculation: "SUM(total_conversions)", format: "integer" } + - { metric_id: "sends", display_name: "Sends", calculation: "SUM(total_sends)", format: "integer" } + - { metric_id: "deliveries", display_name: "Deliveries", calculation: "SUM(total_deliveries)", format: "integer" } + - { metric_id: "open_rate", display_name: "Open Rate", calculation: "SUM(total_opens) / SUM(total_deliveries)", format: "percentage" } + - { metric_id: "click_rate", display_name: "Click Rate", calculation: "SUM(total_clicks) / SUM(total_deliveries)", format: "percentage" } + - { metric_id: "bounce_rate", display_name: "Bounce Rate", calculation: "SUM(total_hard_bounces + total_soft_bounces) / SUM(total_sends)", format: "percentage" } + - orderby_clause_template: "ORDER BY SUM(total_revenue_direct + total_revenue_contributed) DESC, SUM(total_conversions) DESC, SUM(total_clicks) DESC, campaign_id DESC" + - notes: | + - This component ranks campaigns. The final sort order MUST use the provided 'orderby_clause_template'. + - Date filtering MUST use the 'summary_date' column (varchar) with string-based comparison (e.g., WHERE summary_date BETWEEN 'YYYY-MM-DD' AND 'YYYY-MM-DD'). + - The 'Revenue' column to be displayed is dynamic. The agent must construct a CASE statement to show Total, Direct, or Contributed Revenue based on the rules previously discussed. + +- component: + - component_id: "journey_performance_ranking" + - component_type: "table" + - title: "Top 5 Journeys" + - source_tables: ["daily_summary", "event_master"] + - dimensions: + - { id: "journey_name", display_name: "Journey Name" } + - { id: "journey_id", display_name: "Journey ID" } + - metrics: + - { metric_id: "revenue", display_name: "Revenue", format: "currency" } + - { metric_id: "conversions", display_name: "Conversions", calculation: "SUM(total_conversions)", format: "integer" } + - { metric_id: "sends", display_name: "Sends", calculation: "SUM(total_sends)", format: "integer" } + - { metric_id: "deliveries", display_name: "Deliveries", calculation: "SUM(total_deliveries)", format: "integer" } + - { metric_id: "open_rate", display_name: "Open Rate", calculation: "SUM(total_opens) / SUM(total_deliveries)", format: "percentage" } + - { metric_id: "click_rate", display_name: "Click Rate", calculation: "SUM(total_clicks) / SUM(total_deliveries)", format: "percentage" } + - { metric_id: "bounce_rate", display_name: "Bounce Rate", calculation: "SUM(total_hard_bounces + total_soft_bounces) / SUM(total_sends)", format: "percentage" } + - orderby_clause_template: "ORDER BY SUM(total_revenue_direct + total_revenue_contributed) DESC, SUM(total_conversions) DESC, SUM(total_clicks) DESC, journey_id DESC" + - notes: | + - This component ranks journeys. The final sort order MUST use the provided 'orderby_clause_template'. + - Date filtering MUST use the 'summary_date' column (varchar) with string-based comparison (e.g., WHERE summary_date BETWEEN 'YYYY-MM-DD' AND 'YYYY-MM-DD'). + - The 'Revenue' column to be displayed is dynamic. The agent must construct a CASE statement to show Total, Direct, or Contributed Revenue based on the rules previously discussed. + +- component: + - component_id: "performance_trend" + - component_type: "trend_chart" + - title: "Performance Trend" + - tabs: + - tab_name: "Engagement" + metrics: + - { metric_id: "sends", display_name: "Sends", calculation: "SUM(total_sends)" } + - { metric_id: "deliveries", display_name: "Deliveries", calculation: "SUM(total_deliveries)" } + - { metric_id: "clicks", display_name: "Clicks", calculation: "SUM(total_clicks)" } + - tab_name: "Revenue" + metrics: + - { metric_id: "revenue", display_name: "Revenue", calculation: "SUM(total_revenue_direct + total_revenue_contributed)" } + display_condition: "Show tab only if total revenue for the period > 0" + - notes: | + - This component uses the 'summary_date' (varchar) column. For date functions like date_trunc, it MUST be converted using CAST(summary_date AS DATE). + - The SQL agent must dynamically set the time grain based on the length of the date range: + * <=20 days: daily + * 21-89 days: weekly (starting Monday) + * >=90 days: monthly + - The agent must perform zero-padding to ensure a continuous time series. + +- component: + - component_id: "data_methodology_disclaimer" + - component_type: "text_note" + - title: "Data Aggregation Methodology" + - content: | + Note: The open rate and click rate in this report are calculated based on total event counts from the daily_summary table. + When the same email is opened or clicked multiple times, each event is counted separately. + This may result in rates appearing higher than unique open/click rates. + + For more detailed analysis with unique count-based metrics, please refer to the Campaign Details report. + - display_condition: "ALWAYS - This component MUST be displayed at the end of every report." + - notes: | + - CRITICAL: This disclaimer MUST always be rendered at the very end of the report, after all other components. + - This is not optional - it must appear in every OverallSummary report regardless of data or filters. + - Rendering style: Plain text only, no borders, no background color, no box shadow, no bold text. + - Use regular font weight (not bold), normal font size (14-16px). + - Background color: transparent or same as page background (white/light gray). + - No visual highlighting or emphasis - this should blend into the page as simple footer text. + +## 5. Component Rendering Order + +The final output MUST render components in the following order: + +1. Report Title +2. Summary (data-driven insights) +3. kpi_summary +4. campaign_performance_ranking +5. journey_performance_ranking +6. performance_trend +7. **data_methodology_disclaimer** ← MUST BE LAST + +**IMPORTANT:** The data_methodology_disclaimer component MUST always appear at the very end of the report, regardless of other component rendering or data availability. diff --git a/engage-box/roi_reporting/agent/knowledge_bases/engage_roi_reporting.yml b/engage-box/roi_reporting/agent/knowledge_bases/engage_roi_reporting.yml new file mode 100644 index 00000000..269cb867 --- /dev/null +++ b/engage-box/roi_reporting/agent/knowledge_bases/engage_roi_reporting.yml @@ -0,0 +1,19 @@ +name: engage_roi_reporting +database: engage_roi_reporting +tables: + - name: daily_summary + td_query: SELECT * FROM daily_summary + enable_data: false + enable_data_index: false + - name: email_events + td_query: SELECT * FROM email_events + enable_data: false + enable_data_index: false + - name: revenue_table + td_query: SELECT * FROM revenue_table + enable_data: false + enable_data_index: false + - name: events_master + td_query: SELECT * FROM events_master + enable_data: false + enable_data_index: false diff --git a/engage-box/roi_reporting/agent/tools.yml b/engage-box/roi_reporting/agent/tools.yml deleted file mode 100644 index f0a2c5e8..00000000 --- a/engage-box/roi_reporting/agent/tools.yml +++ /dev/null @@ -1,97 +0,0 @@ -# Agent Tool Configuration -# ROI Reporting Agent — AI Agent Foundry tool settings -# -# This file defines all tools to be configured for the agent. -# Replace with your actual database name containing ROI reporting tables -# -# To deploy via CLI: -# tdx llm project create "ROI Reporting Agent" -# tdx agent push . -f - -tools: - # ── Data Access ────────────────────────────────────────── - - - function_name: list_columns - function_description: > - Get data schema. Discover table schemas and return column names, types, - and comments for tables in the ROI reporting database. - target: knowledge_base - target_knowledge_base: engage_roi_reporting - target_function: List columns - - - function_name: query_data - function_description: > - Run Presto/Trino query against the ROI reporting database. - Max 100 rows returned. Use GROUP BY aggregations. Never SELECT *. - If result contains [TRUNCATED], use OFFSET and LIMIT for pagination. - target: knowledge_base - target_knowledge_base: engage_roi_reporting - target_function: Query data directly (Presto SQL) - - - function_name: Read_OverallSummary_Spec - function_description: > - Spec for OverallSummary report. Read the Overall Summary report - specification from knowledge base. - target: knowledge_base - target_knowledge_base: OverallSummary_Spec - target_function: Read - - - function_name: Read_CampaignDetails_Spec - function_description: > - Spec for Individual Campaign Detail Report. Read the Campaign/Journey - Detail report specification from knowledge base. - target: knowledge_base - target_knowledge_base: CampaignDetails_Spec - target_function: Read - - # ── Output ─────────────────────────────────────────────── - - - output_name: renderReactApp - function_name: renderReactApp - function_description: > - Generates React components using Tailwind CSS. Use ONLY when explicitly - requested (React, PDF, Dashboard). STRICT: Single file, export default. - NO external libs (no lucide-react). Use inline SVGs. Self-contained. - Render visual content ONLY; NO print/download buttons (system handled). - output_type: Artifact - artifact_content_type: React - - - output_name: text_in_form - function_name: text_in_form - function_description: > - Out text with MD format. Use this when you want to output an error message. - output_type: Artifact - artifact_content_type: Text - - - output_name: ":plotly:" - function_name: new_plot - function_description: > - Provide visualization for analysis result by rendering charts using Plotly.js. - - Use the color scheme: ["B4E3E3", "ABB3DB", "D9BFDF", "F8E1B0", "8FD6D4", - "828DCA", "C69ED0", "F5D389", "6AC8C6", "5867B8", "B37EC0", "F1C461", - "44BAB8", "2E41A6", "8CC97E", "A05EB0"] - - For charts with more than three categories, actively use updatemenus - - When summarizing multiple analysis, combine relevant charts into a single - dashboard using Plotly's grid layout (e.g., grid: {rows: 2, columns: 2, - pattern: 'independent'}) and ensure no elements overlap - - Prevent text overlap by: - * Include adequate margins: {l: 80, r: 80, t: 100, b: 80} - * For pie charts with small segments (<5%), use textinfo: 'none' and rely - on legend instead of labels - * Set minimum dashboard dimensions: height: 600, width: 1000 - * For grid layouts, use wider domain spacing with 0.1 gap: [0, 0.45] and - [0.55, 1] - output_type: Custom - json_schema: - type: object - properties: - data: - type: array - description: Plotly.js data JSON objects - items: - type: object - layout: - type: object - description: Plotly.js layout JSON object - required: - - data diff --git a/engage-box/roi_reporting/workflows/reporting_agent/README.md b/engage-box/roi_reporting/workflows/reporting_agent/README.md index 750a0ee0..d46a291e 100644 --- a/engage-box/roi_reporting/workflows/reporting_agent/README.md +++ b/engage-box/roi_reporting/workflows/reporting_agent/README.md @@ -57,18 +57,22 @@ The `config.yaml` file allows you to configure: 1. Prepare Revenue data table named `revenue_table` under `engage_roi_reporting` database 1. Edit the `config.yaml` file to match your environment after you download this example - 1. Set email_domains + 1. Set email_domains When you send emails using Engage Studio, a database with a name beginning with "delivery_email_" is generated. Please list that database name under `email_domains` instead of the existing database name, `delivery_email_example_com`. + > **Note**: Only include databases created by Engage Studio. Support notification databases (e.g., `delivery_email_support_*`) lack `campaign_id` and `journey_id` columns and will cause SQL errors in the workflow. + 1. Set endpoint Please set the endpoints that matches the Treasure Data region you are using. In this example, the US region endpoints are set. If you are using the Tokyo region, please comment out the US region endpoints and use the commented out endpoints. 1. Upload your project Upload the project using `td workflow push` command in the same directory as `report_preparation.dig`. - 1. Register Master Key(API Key) to Secret + 1. Register Master Key(API Key) to Secret Please register Master Key to the secret of uploaded project. The name of the Secret is `td.apikey`. + > **Important**: The `td.apikey` secret must be a Master API Key in the format `ACCOUNT_ID/KEY` (e.g., `7060/abc123...`). OAuth session tokens (used by Treasure Studio and `tdx auth`) will NOT work with `td_ddl>` operators and will result in `[AUTHENTICATION_FAILURE] [401:Unauthorized]` errors. + ### Scheduled Execution From 0ab228e71c2e97f77a33afcb0f5fe94e7251fc2c Mon Sep 17 00:00:00 2001 From: "Kona (Koshi Nakamura)" Date: Thu, 2 Apr 2026 13:55:39 +0900 Subject: [PATCH 2/2] Remove unnecessary OAuth vs API key warning from workflow README The workflow secret td.apikey naturally expects a Master API Key; no special warning is needed. Co-Authored-By: Claude Opus 4.6 (1M context) --- engage-box/roi_reporting/workflows/reporting_agent/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/engage-box/roi_reporting/workflows/reporting_agent/README.md b/engage-box/roi_reporting/workflows/reporting_agent/README.md index d46a291e..972998a4 100644 --- a/engage-box/roi_reporting/workflows/reporting_agent/README.md +++ b/engage-box/roi_reporting/workflows/reporting_agent/README.md @@ -71,8 +71,6 @@ The `config.yaml` file allows you to configure: 1. Register Master Key(API Key) to Secret Please register Master Key to the secret of uploaded project. The name of the Secret is `td.apikey`. - > **Important**: The `td.apikey` secret must be a Master API Key in the format `ACCOUNT_ID/KEY` (e.g., `7060/abc123...`). OAuth session tokens (used by Treasure Studio and `tdx auth`) will NOT work with `td_ddl>` operators and will result in `[AUTHENTICATION_FAILURE] [401:Unauthorized]` errors. - ### Scheduled Execution