{
  "repo": "tidbcloud/aitestsuit",
  "binary": "api",
  "binary_path": "cmd/api/main.go",
  "dependencies": {
    "AITestSuitLifecycle.Startup": {
      "infrastructure": {},
      "services": []
    },
    "AITestSuitLifecycle.LoadConfig": {
      "infrastructure": {
        "filesystem": {
          "conditions": [
            "Always"
          ],
          "operations": [
            {
              "operation": "READ FILE",
              "trigger": "Load the YAML config from the -config path before any database or client initialization happens",
              "conditions": [
                "configPath resolves to a readable file"
              ],
              "implementation": "configs.GetConfig() -> configor.Load(config, configPath)"
            }
          ]
        }
      },
      "services": []
    },
    "AITestSuitLifecycle.InitializeDatabase": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Always"
          ],
          "operations": [
            {
              "operation": "OPEN DB HANDLE",
              "trigger": "Open the shared GORM handle from cfg.DBDSN",
              "conditions": [
                "cfg.DBDSN != \"\""
              ],
              "implementation": "entity.InitDB(cfg, true) -> gorm.Open(mysql.Open(cfg.DBDSN), &gorm.Config{})"
            },
            {
              "operation": "AUTO MIGRATE",
              "trigger": "Auto-migrate test plan, case, prompt template, run, report, case result, and AI model tables on startup",
              "conditions": [
                "migrate == true"
              ],
              "implementation": "entity.InitDB(cfg, true) -> db.AutoMigrate(&TestPlan{}, &TestCase{}, &PromptTemplate{}, &TestRun{}, &TestReport{}, &TestCaseResult{}, &AIModel{})"
            }
          ]
        }
      },
      "services": []
    },
    "AITestSuitLifecycle.InitializeDAO": {
      "infrastructure": {},
      "services": []
    },
    "AITestSuitLifecycle.InitializeOpenAIClient": {
      "infrastructure": {},
      "services": []
    },
    "AITestSuitLifecycle.InitializeDataAPIClient": {
      "infrastructure": {},
      "services": [
        {
          "name": "dataservices-dataapi-grpc",
          "target_repo": "tidbcloud/dataservices",
          "target_interface": "DataAPIService",
          "operation_type": "grpc_connect",
          "conditions": [
            "Always"
          ],
          "operations": [
            {
              "operation": "CONNECT",
              "trigger": "Build the shared DataAPIService gRPC client used later by the worker SQL execution path",
              "conditions": [
                "cfg.DataAPIAddr != \"\""
              ],
              "implementation": "dataapisvc.InitDataAPI(cfg.DataAPIAddr) -> grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials())); dataapi.NewDataAPIServiceClient(conn)"
            }
          ]
        }
      ]
    },
    "AITestSuitLifecycle.InitializeAIPowerClient": {
      "infrastructure": {},
      "services": [
        {
          "name": "aipower-grpc",
          "target_repo": "tidbcloud/aipower",
          "target_interface": "ChatBotService",
          "operation_type": "grpc_connect",
          "conditions": [
            "Always"
          ],
          "operations": [
            {
              "operation": "CONNECT",
              "trigger": "Build the shared ChatBotService client used by prompt APIs and the worker task execution path",
              "conditions": [
                "cfg.AIPowerAddr != \"\""
              ],
              "implementation": "aipowersvc.InitAIPower(cfg.AIPowerAddr) -> tigrpc.NewClientConn(addr, tigrpc.WithDefault()); aipower.NewChatBotServiceClient(conn)"
            }
          ]
        }
      ]
    },
    "AITestSuitLifecycle.StartWorker": {
      "infrastructure": {},
      "services": [
        {
          "name": "aitestsuit-worker",
          "target_repo": "tidbcloud/aitestsuit",
          "target_interface": "AITestSuitWorker.ConsumeQueue",
          "operation_type": "inprocess_async",
          "conditions": [
            "Always"
          ],
          "operations": [
            {
              "operation": "START GOROUTINE",
              "trigger": "Start the background queue consumer before the HTTP server begins serving requests",
              "conditions": [
                "Always"
              ],
              "implementation": "go tasks.StartWorker(ctx)"
            }
          ]
        }
      ]
    },
    "AITestSuitLifecycle.StartHTTPServer": {
      "infrastructure": {},
      "services": []
    },
    "AITestSuitWorker.ConsumeQueue": {
      "infrastructure": {},
      "services": [
        {
          "name": "aitestsuit-worker",
          "target_repo": "tidbcloud/aitestsuit",
          "target_interface": "AITestSuitWorker.RunTask",
          "operation_type": "inprocess_async",
          "conditions": [
            "Only after a task has been enqueued into msgCh"
          ],
          "operations": [
            {
              "operation": "DEQUEUE + DISPATCH",
              "trigger": "Read a queued task from msgCh and dispatch runTask in a new goroutine",
              "conditions": [
                "t := <-msgCh succeeds"
              ],
              "implementation": "tasks.StartWorker(ctx) -> t := <-msgCh; go runTask(ctx, t)"
            }
          ]
        }
      ]
    },
    "AITestSuitWorker.RunTask": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Always for report status updates and AI model lookup; per-result writes happen once each workerpool job finishes"
          ],
          "operations": [
            {
              "operation": "UPDATE",
              "trigger": "Mark the report as running before starting per-case execution",
              "conditions": [
                "Always"
              ],
              "implementation": "dao.UpdateTestReport(ctx, &report)"
            },
            {
              "operation": "SELECT",
              "trigger": "Load the test plan to find the AI model configured for the task",
              "conditions": [
                "Always"
              ],
              "implementation": "dao.GetTestPlanByID(ctx, t.TestPlanID)"
            },
            {
              "operation": "SELECT",
              "trigger": "Load the AI model to pick the model type used for ChatBot requests",
              "conditions": [
                "test plan lookup succeeded"
              ],
              "implementation": "dao.GetAIModelByID(ctx, testPlan.AIModelID)"
            },
            {
              "operation": "SELECT",
              "trigger": "Load each test case before generating SQL and comparing results",
              "conditions": [
                "Per queued case result",
                "tryRunTask(...) or compareTaskResult(...) is reached"
              ],
              "implementation": "dao.GetTestCaseByID(ctx, result.TestCaseID)"
            },
            {
              "operation": "SELECT",
              "trigger": "Load the selected prompt template before calling aipower",
              "conditions": [
                "Per queued case result",
                "tryRunTask(...) is reached"
              ],
              "implementation": "dao.GetPromptTemplateByID(ctx, t.PromptTemplateID)"
            },
            {
              "operation": "UPDATE",
              "trigger": "Persist each case execution result after the workerpool job finishes",
              "conditions": [
                "Per queued case result",
                "The workerpool job reaches dao.UpdateTestCaseResult"
              ],
              "implementation": "dao.UpdateTestCaseResult(ctx, &result)"
            },
            {
              "operation": "UPDATE",
              "trigger": "Persist the final report metrics, pass rate, token usage, and end time after all jobs complete",
              "conditions": [
                "wp.StopWait() returned"
              ],
              "implementation": "dao.UpdateTestReport(ctx, &report)"
            }
          ]
        }
      },
      "services": [
        {
          "name": "aipower-chatbot",
          "target_repo": "tidbcloud/aipower",
          "target_interface": "ChatBotService.ChatBot",
          "operation_type": "grpc_unary",
          "conditions": [
            "Per queued case result",
            "The worker successfully loaded both the test case and the prompt template"
          ],
          "operations": [
            {
              "operation": "RPC CALL",
              "trigger": "Generate SQL or chart completion for the current test case via aipower",
              "conditions": [
                "dao.GetTestCaseByID(...) succeeded",
                "dao.GetPromptTemplateByID(...) succeeded"
              ],
              "implementation": "aipowersvc.GenSQLByTestCase(...) -> aipowerClient.ChatBot(ctx, &req)"
            }
          ]
        },
        {
          "name": "dataservices-dataapi",
          "target_repo": "tidbcloud/dataservices",
          "target_interface": "DataAPIService.ExecSqlDirectly",
          "operation_type": "grpc_unary",
          "conditions": [
            "Only after aipower returns Code == 0 for the generated SQL path, or when the test case has a standard SQL for comparison"
          ],
          "operations": [
            {
              "operation": "RPC CALL",
              "trigger": "Execute the SQL generated by aipower against the target database",
              "conditions": [
                "aiPowerResp != nil",
                "aiPowerResp.Code == 0"
              ],
              "implementation": "dataapisvc.TryExecSQL(ctx, cfg.ProjectID, cfg.ClusterID, testCase.Database, sql) -> dataapiClient.ExecSqlDirectly(ctx, req)"
            },
            {
              "operation": "RPC CALL",
              "trigger": "Execute the standard SQL from the test case for result comparison",
              "conditions": [
                "testCase.SQL != \"\"",
                "sqlResp != nil"
              ],
              "implementation": "compareTaskResult(...) -> dataapisvc.TryExecSQL(ctx, cfg.ProjectID, cfg.ClusterID, testCase.Database, testCase.SQL) -> dataapiClient.ExecSqlDirectly(ctx, req)"
            }
          ]
        }
      ]
    },
    "HTTP GET /healthz": {
      "infrastructure": {},
      "services": []
    },
    "HTTP GET /ping": {
      "infrastructure": {},
      "services": []
    },
    "HTTP GET /*filepath": {
      "infrastructure": {
        "filesystem": {
          "conditions": [
            "Only when the request path is not handled earlier by /healthz, /ping, or /api/v1/* and the static middleware is reached"
          ],
          "operations": [
            {
              "operation": "READ FILE",
              "trigger": "Serve the built frontend assets rooted at ./ui/dist",
              "conditions": [
                "The requested path resolves to an existing file under ./ui/dist"
              ],
              "implementation": "static.Serve(\"/\", static.LocalFile(\"./ui/dist/\", true))"
            }
          ]
        }
      },
      "services": []
    },
    "HTTP GET /api/v1/plans": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Always"
          ],
          "operations": [
            {
              "operation": "SELECT",
              "trigger": "Fetch the current page of test plans and the total count",
              "conditions": [
                "Always"
              ],
              "implementation": "dao.GetAllTestPlansByPage(c, limit, offset)"
            },
            {
              "operation": "SELECT",
              "trigger": "Count cases, runs, and reports plus load the last report for each returned plan",
              "conditions": [
                "Per returned plan"
              ],
              "implementation": "dao.MustCountCasesByPlanID(ctx, plan.ID); dao.MustCountRunsByPlanID(ctx, plan.ID); dao.MustCountReportsByPlanID(ctx, plan.ID); dao.GetLastTestReportByPlanID(ctx, plan.ID)"
            },
            {
              "operation": "SELECT",
              "trigger": "Load the AI model associated with each returned plan",
              "conditions": [
                "Per returned plan"
              ],
              "implementation": "dao.GetAIModelByID(ctx, plan.AIModelID)"
            },
            {
              "operation": "SELECT",
              "trigger": "Load all reports for each returned plan to derive the average duration over valid completed runs",
              "conditions": [
                "Per returned plan"
              ],
              "implementation": "dao.GetAllReportsByPlanID(ctx, plan.ID)"
            }
          ]
        }
      },
      "services": []
    },
    "HTTP GET /api/v1/plan/:plan_id/cases": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Only after plan_id binds from the URI"
          ],
          "operations": [
            {
              "operation": "SELECT",
              "trigger": "Fetch the paginated test cases for the requested plan",
              "conditions": [
                "c.ShouldBindUri(&req) succeeded"
              ],
              "implementation": "dao.GetTestCasesByPlanIDAndPage(c, req.PlanID, limit, offset)"
            },
            {
              "operation": "SELECT",
              "trigger": "Load all historical case results to compute pass rate for each returned case",
              "conditions": [
                "Per returned test case"
              ],
              "implementation": "dao.GetAllTestCaseResultsByCaseID(ctx, testCase.ID)"
            }
          ]
        }
      },
      "services": []
    },
    "HTTP GET /api/v1/case/:id": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Only after id binds from the URI"
          ],
          "operations": [
            {
              "operation": "SELECT",
              "trigger": "Load the requested test case",
              "conditions": [
                "c.ShouldBindUri(&uriParams) succeeded"
              ],
              "implementation": "dao.GetTestCaseByID(ctx, uriParams.ID)"
            }
          ]
        }
      },
      "services": []
    },
    "HTTP GET /api/v1/prompt_templates": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Always"
          ],
          "operations": [
            {
              "operation": "SELECT",
              "trigger": "Fetch prompt templates using pagination",
              "conditions": [
                "Always"
              ],
              "implementation": "dao.GetPromptTemplates(ctx, limit, offset)"
            }
          ]
        }
      },
      "services": []
    },
    "HTTP POST /api/v1/prompt_template": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Only after JSON binding succeeds"
          ],
          "operations": [
            {
              "operation": "INSERT",
              "trigger": "Create the prompt template row",
              "conditions": [
                "c.ShouldBindJSON(&req) succeeded"
              ],
              "implementation": "dao.CreatePromptTemplate(ctx, &template)"
            }
          ]
        }
      },
      "services": []
    },
    "HTTP PUT /api/v1/prompt_template/:id": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Only after both URI binding and JSON binding succeed"
          ],
          "operations": [
            {
              "operation": "SELECT",
              "trigger": "Load the existing prompt template before overwriting its fields",
              "conditions": [
                "c.ShouldBindUri(&req) succeeded",
                "c.ShouldBindJSON(&templateReq) succeeded"
              ],
              "implementation": "dao.GetPromptTemplateByID(ctx, req.ID)"
            },
            {
              "operation": "UPDATE",
              "trigger": "Persist the updated prompt template version and content",
              "conditions": [
                "dao.GetPromptTemplateByID(...) succeeded"
              ],
              "implementation": "dao.UpdatePromptTemplate(ctx, &template)"
            }
          ]
        }
      },
      "services": []
    },
    "HTTP GET /api/v1/reports": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Only after query binding for plan_id/run_id succeeds"
          ],
          "operations": [
            {
              "operation": "SELECT",
              "trigger": "Fetch paginated reports with optional plan_id and run_id filters",
              "conditions": [
                "c.ShouldBindQuery(&filter) succeeded"
              ],
              "implementation": "dao.GetTestReports(ctx, filter, limit, offset)"
            },
            {
              "operation": "SELECT",
              "trigger": "Load the related plan, run, and prompt template for each returned report",
              "conditions": [
                "Per returned report"
              ],
              "implementation": "dao.GetTestPlanByID(ctx, report.PlanID); dao.GetTestRunByID(ctx, report.TestRunID); dao.GetPromptTemplateByID(ctx, report.PromptTemplateID)"
            }
          ]
        }
      },
      "services": []
    },
    "HTTP GET /api/v1/report/:id": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Only after report id URI binding and report-detail query binding succeed"
          ],
          "operations": [
            {
              "operation": "SELECT",
              "trigger": "Load the requested report",
              "conditions": [
                "c.ShouldBindUri(&req) succeeded",
                "c.ShouldBindQuery(&filter) succeeded"
              ],
              "implementation": "dao.GetTestReport(ctx, req.ID)"
            },
            {
              "operation": "SELECT",
              "trigger": "Fetch paginated case results for the report, optionally filtered by ResultA",
              "conditions": [
                "dao.GetTestReport(...) succeeded"
              ],
              "implementation": "dao.GetTestResultsByReportIDAndPage(ctx, req.ID, filter, limit, offset)"
            },
            {
              "operation": "SELECT",
              "trigger": "Load the referenced test case for each returned case result",
              "conditions": [
                "Per returned case result"
              ],
              "implementation": "dao.GetTestCaseByID(ctx, caseResult.TestCaseID)"
            }
          ]
        }
      },
      "services": []
    },
    "HTTP GET /api/v1/runs": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Always"
          ],
          "operations": [
            {
              "operation": "SELECT",
              "trigger": "Fetch paginated test runs",
              "conditions": [
                "Always"
              ],
              "implementation": "dao.GetTestRuns(ctx, limit, offset)"
            }
          ]
        }
      },
      "services": []
    },
    "HTTP GET /api/v1/run/:id": {
      "infrastructure": {},
      "services": []
    },
    "HTTP GET /api/v1/stats": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Only after stats query binding succeeds"
          ],
          "operations": [
            {
              "operation": "SELECT",
              "trigger": "Fetch paginated test runs that will each be joined against the requested plan list",
              "conditions": [
                "c.ShouldBindQuery(&req) succeeded"
              ],
              "implementation": "dao.GetTestRuns(ctx, limit, offset)"
            },
            {
              "operation": "SELECT",
              "trigger": "Resolve each requested plan id before looking up the latest completed report for the run-plan pair",
              "conditions": [
                "Per parsed plan id",
                "ParseUint(planIDStr, 10, 64) result is used even when it falls back to 0 on parse error"
              ],
              "implementation": "dao.GetTestPlanByID(ctx, planID)"
            },
            {
              "operation": "SELECT",
              "trigger": "Load the latest completed report for each run-plan combination",
              "conditions": [
                "Per returned run",
                "Per parsed plan id",
                "dao.GetTestPlanByID(...) did not fail with a non-ErrRecordNotFound error"
              ],
              "implementation": "dao.GetLastTestReportByRunIDAndPlanIDAndStatus(ctx, run.ID, plan.ID, consts.StatusOver)"
            }
          ]
        }
      },
      "services": []
    },
    "HTTP POST /api/v1/run": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Only after JSON binding succeeds"
          ],
          "operations": [
            {
              "operation": "INSERT",
              "trigger": "Create the test run row",
              "conditions": [
                "c.ShouldBindJSON(&req) succeeded"
              ],
              "implementation": "dao.CreateTestRun(ctx, &testRun)"
            }
          ]
        }
      },
      "services": []
    },
    "HTTP POST /api/v1/task": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Only after the task payload binds successfully and all prerequisite DB reads/inserts succeed"
          ],
          "operations": [
            {
              "operation": "SELECT",
              "trigger": "Load the requested test run and test plan before creating a report",
              "conditions": [
                "c.ShouldBindJSON(&task) succeeded"
              ],
              "implementation": "dao.GetTestRun(ctx, task.TestRunID); dao.GetTestPlanByID(ctx, task.TestPlanID)"
            },
            {
              "operation": "SELECT",
              "trigger": "Load all test cases for the selected plan in order to seed per-case results",
              "conditions": [
                "dao.GetTestRun(...) succeeded",
                "dao.GetTestPlanByID(...) succeeded"
              ],
              "implementation": "dao.GetAllTestCasesByPlanID(ctx, plan.ID)"
            },
            {
              "operation": "INSERT",
              "trigger": "Create the report row that will be updated later by the background worker",
              "conditions": [
                "dao.GetAllTestCasesByPlanID(...) succeeded"
              ],
              "implementation": "dao.CreateTestReport(ctx, &report)"
            },
            {
              "operation": "INSERT",
              "trigger": "Create one pending TestCaseResult row for every case in the plan",
              "conditions": [
                "dao.CreateTestReport(...) succeeded",
                "Per returned test case"
              ],
              "implementation": "dao.CreateTestCaseResult(ctx, &result)"
            }
          ]
        }
      },
      "services": [
        {
          "name": "aitestsuit-worker",
          "target_repo": "tidbcloud/aitestsuit",
          "target_interface": "AITestSuitWorker.ConsumeQueue",
          "operation_type": "inprocess_async",
          "conditions": [
            "Only after the report row and all case-result rows have been created successfully"
          ],
          "operations": [
            {
              "operation": "ENQUEUE",
              "trigger": "Send the prepared task, report, and result list to the background worker queue",
              "conditions": [
                "dao.CreateTestReport(...) succeeded",
                "All dao.CreateTestCaseResult(...) calls succeeded"
              ],
              "implementation": "tasks.EnqueueTask(ctx, task) -> msgCh <- t"
            }
          ]
        }
      ]
    },
    "HTTP POST /api/v1/dataset": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Only when req.Name matches question520 or spider; unknown dataset names return success without persistence"
          ],
          "operations": [
            {
              "operation": "INSERT",
              "trigger": "Create a synthetic test plan for the embedded dataset import",
              "conditions": [
                "req.Name == consts.DataSetQuestion520 or req.Name == consts.DataSetSpider"
              ],
              "implementation": "dataset.GenQuestion(ctx, req.Name) -> dao.CreateTestPlan(ctx, &plan)"
            },
            {
              "operation": "INSERT",
              "trigger": "Create one test case row per embedded dataset question",
              "conditions": [
                "req.Name == consts.DataSetQuestion520 or req.Name == consts.DataSetSpider",
                "Per embedded question item"
              ],
              "implementation": "dataset.GenQuestion(ctx, req.Name) -> dao.CreateTestCase(ctx, &testCase)"
            }
          ]
        }
      },
      "services": []
    },
    "HTTP POST /api/v1/plan": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Only after JSON binding succeeds"
          ],
          "operations": [
            {
              "operation": "INSERT",
              "trigger": "Create the test plan row",
              "conditions": [
                "c.ShouldBindJSON(&payload) succeeded"
              ],
              "implementation": "dao.CreateTestPlan(ctx, &testPlan)"
            }
          ]
        }
      },
      "services": []
    },
    "HTTP POST /api/v1/case": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Only after JSON binding succeeds"
          ],
          "operations": [
            {
              "operation": "INSERT",
              "trigger": "Create the test case row",
              "conditions": [
                "c.ShouldBindJSON(&payload) succeeded"
              ],
              "implementation": "dao.CreateTestCase(ctx, &testCase)"
            }
          ]
        }
      },
      "services": []
    },
    "HTTP PUT /api/v1/case": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Only after JSON binding succeeds"
          ],
          "operations": [
            {
              "operation": "SELECT",
              "trigger": "Load the existing test case before overwriting its fields",
              "conditions": [
                "c.ShouldBindJSON(&payload) succeeded"
              ],
              "implementation": "dao.GetTestCaseByID(ctx, payload.ID)"
            },
            {
              "operation": "UPDATE",
              "trigger": "Persist the updated test case fields",
              "conditions": [
                "dao.GetTestCaseByID(...) succeeded"
              ],
              "implementation": "dao.UpdateTestCase(ctx, &testCase)"
            }
          ]
        }
      },
      "services": []
    },
    "HTTP DELETE /api/v1/case/:id": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Only after id binds from the URI"
          ],
          "operations": [
            {
              "operation": "DELETE",
              "trigger": "Delete the requested test case row using Unscoped delete",
              "conditions": [
                "c.ShouldBindUri(&uriParams) succeeded"
              ],
              "implementation": "dao.DeleteTestCaseByID(ctx, uriParams.ID)"
            }
          ]
        }
      },
      "services": []
    },
    "HTTP POST /api/v1/ai_model": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Only after JSON binding succeeds"
          ],
          "operations": [
            {
              "operation": "INSERT",
              "trigger": "Create the AI model row",
              "conditions": [
                "c.ShouldBindJSON(&payload) succeeded"
              ],
              "implementation": "dao.CreateAIModel(ctx, &aiModel)"
            }
          ]
        }
      },
      "services": []
    },
    "HTTP PUT /api/v1/ai_model": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Only after JSON binding succeeds"
          ],
          "operations": [
            {
              "operation": "SELECT",
              "trigger": "Load the existing AI model before overwriting its fields",
              "conditions": [
                "c.ShouldBindJSON(&payload) succeeded"
              ],
              "implementation": "dao.GetAIModelByID(ctx, payload.ID)"
            },
            {
              "operation": "UPDATE",
              "trigger": "Persist the updated AI model fields",
              "conditions": [
                "dao.GetAIModelByID(...) succeeded"
              ],
              "implementation": "dao.UpdateAIModel(ctx, &aiModel)"
            }
          ]
        }
      },
      "services": []
    },
    "HTTP GET /api/v1/ai_models": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Always"
          ],
          "operations": [
            {
              "operation": "SELECT",
              "trigger": "Fetch paginated AI models",
              "conditions": [
                "Always"
              ],
              "implementation": "dao.GetAIModels(ctx, limit, offset)"
            }
          ]
        }
      },
      "services": []
    },
    "HTTP GET /api/v1/aipower/latest_prompt": {
      "infrastructure": {},
      "services": [
        {
          "name": "aipower-chatbot",
          "target_repo": "tidbcloud/aipower",
          "target_interface": "ChatBotService.GetLatestPrompt",
          "operation_type": "grpc_unary",
          "conditions": [
            "Only after ai_model and deployment query binding succeeds"
          ],
          "operations": [
            {
              "operation": "RPC CALL",
              "trigger": "Fetch the latest prompt for the requested ai_model/deployment pair",
              "conditions": [
                "c.ShouldBindQuery(&filter) succeeded"
              ],
              "implementation": "aipowersvc.GetCurrentPromptTemplate(ctx, filter.AIModel, filter.Deployment) -> aipowerClient.GetLatestPrompt(ctx, &req)"
            }
          ]
        }
      ]
    },
    "HTTP GET /api/v1/aipower/prompts": {
      "infrastructure": {},
      "services": [
        {
          "name": "aipower-chatbot",
          "target_repo": "tidbcloud/aipower",
          "target_interface": "ChatBotService.GetAllPromptByAIModel",
          "operation_type": "grpc_unary",
          "conditions": [
            "Only after ai_model and deployment query binding succeeds"
          ],
          "operations": [
            {
              "operation": "RPC CALL",
              "trigger": "Fetch all prompts for the requested ai_model/deployment pair",
              "conditions": [
                "c.ShouldBindQuery(&filter) succeeded"
              ],
              "implementation": "aipowersvc.GetAllPromptTemplate(ctx, filter.AIModel, filter.Deployment) -> aipowerClient.GetAllPromptByAIModel(ctx, &req)"
            }
          ]
        }
      ]
    },
    "HTTP POST /api/v1/aipower/prompts/release": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Only after JSON binding succeeds"
          ],
          "operations": [
            {
              "operation": "SELECT",
              "trigger": "Load the local prompt template before releasing it to aipower",
              "conditions": [
                "c.ShouldBindJSON(&req) succeeded"
              ],
              "implementation": "dao.GetPromptTemplateByID(ctx, req.ID)"
            }
          ]
        }
      },
      "services": [
        {
          "name": "aipower-chatbot",
          "target_repo": "tidbcloud/aipower",
          "target_interface": "ChatBotService.SavePrompt",
          "operation_type": "grpc_unary",
          "conditions": [
            "Only after the local prompt template row has been loaded successfully"
          ],
          "operations": [
            {
              "operation": "RPC CALL",
              "trigger": "Release the local prompt template to aipower for the selected ai_model/deployment pair",
              "conditions": [
                "dao.GetPromptTemplateByID(...) succeeded"
              ],
              "implementation": "aipowersvc.SavePromptTemplate(ctx, req.AIModel, req.Deployment, template) -> aipowerClient.SavePrompt(ctx, &req)"
            }
          ]
        }
      ]
    }
  }
}
