{
  "repo": "tidbcloud/aipower",
  "binary": "api",
  "binary_path": "cmd/api/main.go",
  "dependencies": {
    "AIPowerLifecycle.LoadFlags": {
      "infrastructure": {},
      "services": []
    },
    "AIPowerLifecycle.InitConfig": {
      "infrastructure": {
        "filesystem": {
          "conditions": [
            "Always"
          ],
          "operations": [
            {
              "operation": "READ",
              "trigger": "Load the aipower configuration file from the config-path flag",
              "conditions": [
                "Always after flag parsing"
              ],
              "implementation": "config.InitConfig(ctx, configPath)"
            }
          ]
        }
      },
      "services": []
    },
    "AIPowerLifecycle.InitOpenAIClient": {
      "infrastructure": {},
      "services": []
    },
    "AIPowerLifecycle.InitRedisClient": {
      "infrastructure": {},
      "services": []
    },
    "AIPowerLifecycle.ConnectMySQL": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "config.InitConfig(ctx, configPath) succeeded"
          ],
          "operations": [
            {
              "operation": "CONNECT",
              "trigger": "Open the GORM MySQL connection used by prompt, rate-limit, and bot-interact DAOs",
              "conditions": [
                "Always after config load succeeds"
              ],
              "implementation": "database.Connect(ctx, conf.MysqlConf.User, conf.MysqlConf.Password, conf.MysqlConf.Host, conf.MysqlConf.Port, conf.MysqlConf.Database, conf.MysqlConf.CharSet, conf.MysqlConf.TimeZone)"
            }
          ]
        }
      },
      "services": []
    },
    "AIPowerLifecycle.RunMigrate": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "config.Global().ServerConf.RunMigrate == true"
          ],
          "operations": [
            {
              "operation": "AUTO_MIGRATE",
              "trigger": "Apply schema migrations for aipower entities",
              "conditions": [
                "Only when server.runMigrate is enabled"
              ],
              "implementation": "database.Migrate() -> DBConn.DB.AutoMigrate(entity.Models...)"
            }
          ]
        }
      },
      "services": []
    },
    "AIPowerLifecycle.InitMeasureReport": {
      "infrastructure": {
        "scheduler": {
          "conditions": [
            "database initialization completed"
          ],
          "operations": [
            {
              "operation": "START_BACKGROUND_WORKER",
              "trigger": "Initialize measure reporting and start the scheduled measure sender worker",
              "conditions": [
                "Always after database setup"
              ],
              "implementation": "measure_report.InitMeasure(config.Global().SentryConf.Environment, \"\") -> Report.InitMeasureSQS(...); RunWorker()"
            }
          ]
        }
      },
      "services": []
    },
    "AIPowerLifecycle.SetupSentry": {
      "infrastructure": {
        "sentry": {
          "conditions": [
            "startup(ctx) invoked"
          ],
          "operations": [
            {
              "operation": "INIT",
              "trigger": "Initialize logger and Sentry integration for the api process",
              "conditions": [
                "Always during startup"
              ],
              "implementation": "tisentry.SetupLoggerAndSentry(config.Global().ServerConf.LogLevel, sentryConfig.LogLevel, sentryConfig.DSN, sentryConfig.Environment)"
            }
          ]
        }
      },
      "services": []
    },
    "AIPowerLifecycle.StartupTracing": {
      "infrastructure": {
        "jaeger": {
          "conditions": [
            "startup(ctx) invoked"
          ],
          "operations": [
            {
              "operation": "INIT",
              "trigger": "Initialize Jaeger tracing for aipower-api",
              "conditions": [
                "Always in the tracing goroutine"
              ],
              "implementation": "titracing.InitJaeger(componentName)"
            }
          ]
        }
      },
      "services": []
    },
    "AIPowerLifecycle.StartupMetrics": {
      "infrastructure": {
        "http_server": {
          "conditions": [
            "startup(ctx) invoked"
          ],
          "operations": [
            {
              "operation": "LISTEN_AND_SERVE",
              "trigger": "Start the dedicated metrics HTTP listener",
              "conditions": [
                "Always in the metrics goroutine"
              ],
              "implementation": "mEng.Any(\"/metrics\", prometheusHandler()); mEng.Run(\"0.0.0.0:\" + config.Global().ServerConf.MetricsPort)"
            }
          ]
        }
      },
      "services": []
    },
    "AIPowerLifecycle.StartupHTTP": {
      "infrastructure": {
        "http_server": {
          "conditions": [
            "startup(ctx) invoked"
          ],
          "operations": [
            {
              "operation": "LISTEN_AND_SERVE",
              "trigger": "Register the central HTTP routes and start the dedicated HTTP API listener",
              "conditions": [
                "Always in the HTTP goroutine"
              ],
              "implementation": "metrics.RegisterCentralMetrics(); httpapi.RouteCentralAPI(eng); eng.Run(\"0.0.0.0:\" + config.Global().ServerConf.HttpPort)"
            }
          ]
        }
      },
      "services": []
    },
    "AIPowerLifecycle.BuildGRPCServer": {
      "infrastructure": {},
      "services": []
    },
    "AIPowerLifecycle.RegisterGRPCServices": {
      "infrastructure": {},
      "services": []
    },
    "AIPowerLifecycle.StartupGRPC": {
      "infrastructure": {
        "grpc_server": {
          "conditions": [
            "startup(ctx) invoked"
          ],
          "operations": [
            {
              "operation": "LISTEN_AND_SERVE",
              "trigger": "Listen on the configured gRPC port, register reflection, and serve ChatBotService",
              "conditions": [
                "Always after BuildGRPCServer and RegisterGRPCServices"
              ],
              "implementation": "listen, err := net.Listen(\"tcp\", \":\"+config.Global().ServerConf.GrpcPort); reflection.Register(grpcServer); grpcServer.Serve(listen)"
            }
          ]
        }
      },
      "services": []
    },
    "HealthAPI.Healthz": {
      "infrastructure": {},
      "services": []
    },
    "SwaggerAPI.SwaggerUI": {
      "infrastructure": {},
      "services": []
    },
    "CentralAPI.InteractBot": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "ginparser url validation succeeded",
            "params.BindParam(req) succeeded",
            "openaisvc.CheckInteractReqeustValid(req) succeeded"
          ],
          "operations": [
            {
              "operation": "SELECT",
              "trigger": "Validate an existing session against previously saved interaction records",
              "conditions": [
                "req.SessionID != \"\""
              ],
              "implementation": "dao.GetBotInteractRecordBySessionIDCreatedBy(req.SessionID, createdBy)"
            },
            {
              "operation": "INSERT",
              "trigger": "Persist a failed synchronous interaction attempt",
              "conditions": [
                "openaisvc.InteractBot returns err != nil",
                "req.Async == false"
              ],
              "implementation": "saveRequestRecord(ctx, req, prompt, completion, createdBy, errorMsg, executeTime) -> dao.CreateBotInteractRecord(record)"
            },
            {
              "operation": "INSERT",
              "trigger": "Persist the final stream result when stream mode is enabled",
              "conditions": [
                "config.Global().ServerConf.EnableStreamMode == true"
              ],
              "implementation": "requestOpenAIInStream(...) defer saveRequestRecord(ctx, req, prompt, completionResp.Completion, req.Caller, errMsg, endAt.Sub(startAt).Milliseconds())"
            }
          ]
        },
        "redis": {
          "conditions": [
            "config.Global().ServerConf.EnableStreamMode == true"
          ],
          "operations": [
            {
              "operation": "SETEX",
              "trigger": "Persist streaming progress and final completion state by job ID",
              "conditions": [
                "OpenAI stream emits chunks or finishes"
              ],
              "implementation": "postHandleStreamCompletion(...) -> cachesvc.SaveCompletionResult(ctx, jobID, completionResp)"
            }
          ]
        }
      },
      "services": [
        {
          "name": "dataservices",
          "target_repo": "tidbcloud/dataservices",
          "target_interface": "DataAPIService.GetClusterMeta / GetDatabaseMeta / ExecSqlDirectly",
          "operation_type": "grpc_unary",
          "conditions": [
            "openaisvc.CheckInteractReqeustValid(req) succeeded"
          ],
          "operations": [
            {
              "operation": "GetClusterMeta",
              "trigger": "Load cluster-wide metadata to guess the database when req.Database is empty for non-chat2sql flows",
              "conditions": [
                "req.Database == \"\"",
                "req.BotType != chat2sql"
              ],
              "implementation": "createCompletionForGuessDatabase(ctx, &guessDBReq) -> grpc.GetClusterMeta(ctx, req)"
            },
            {
              "operation": "GetDatabaseMeta",
              "trigger": "Load table metadata for database-scoped prompt generation when information-schema mode is disabled",
              "conditions": [
                "req.Database != \"\" or guessed database succeeded",
                "utils.GetInformationSchemaEnable(ctx, req.BotType) == false"
              ],
              "implementation": "getSqlEditorDatabaseColumns(ctx, req) -> grpc.GetDatabaseMeta(ctx, metaReq)"
            },
            {
              "operation": "ExecSqlDirectly",
              "trigger": "Load detailed schema metadata or run SQL dry-run validation through dataservices",
              "conditions": [
                "utils.GetInformationSchemaEnable(ctx, req.BotType) == true for metadata fetch",
                "validateSQLWithDryRun is reached when the generated SQL contains GROUP BY and sql_mode relaxation is enabled"
              ],
              "implementation": "getDatabaseDetailedMeta(ctx, req) / validateSQLWithDryRun(ctx, req, sql) -> grpc.GetDatabaseDetailedMeta(ctx, metaReq) / grpc.ValidateSQLWithDryRun(ctx, metaReq)"
            }
          ]
        },
        {
          "name": "openai",
          "target_repo": "api.openai.com",
          "target_interface": "CreateChatCompletion / CreateChatCompletionStream / CreateCompletion / CreateCompletionStream",
          "operation_type": "http_post",
          "conditions": [
            "openaisvc.CheckInteractReqeustValid(req) succeeded",
            "prompt generation succeeded"
          ],
          "operations": [
            {
              "operation": "CreateChatCompletionStream / CreateCompletionStream",
              "trigger": "Generate the response in stream mode and progressively persist it by job ID",
              "conditions": [
                "config.Global().ServerConf.EnableStreamMode == true"
              ],
              "implementation": "RequestOpenAIInStream(ctx, req, prompt, params, jobID, timeout) -> requestOpenAIInStreamWithGPT35(...) / requestOpenAIInStreamWithDavinci(...)"
            },
            {
              "operation": "CreateChatCompletion / CreateCompletion",
              "trigger": "Generate the response synchronously when stream mode is disabled",
              "conditions": [
                "config.Global().ServerConf.EnableStreamMode == false"
              ],
              "implementation": "requestOpenAI(ctx, req, prompt, params) -> openAIClient.CreateChatCompletion(ctx, openAIReq) / openAIClient.CreateCompletion(ctx, openAIReq)"
            }
          ]
        }
      ]
    },
    "CentralAPI.GetAPIKeyInfo": {
      "infrastructure": {},
      "services": [
        {
          "name": "account-provider",
          "target_repo": "tidbcloud/account",
          "target_interface": "AccountAPIService.GetApiKeyByAccessKey",
          "operation_type": "grpc_unary",
          "conditions": [
            "c.ShouldBindQuery(params) succeeded"
          ],
          "operations": [
            {
              "operation": "GetApiKeyByAccessKey",
              "trigger": "Resolve the access key into resource infos and extract org-scoped resource IDs",
              "conditions": [
                "Always after query binding succeeds"
              ],
              "implementation": "grpc.GetApiKeyByAccessKey(ctx, &account.GetApiKeyByAccessKeyReq{AccessKey: params.AccessKey})"
            }
          ]
        }
      ]
    },
    "CentralAPI.GetClusterInfo": {
      "infrastructure": {},
      "services": [
        {
          "name": "core-svc",
          "target_repo": "tidbcloud/core-svc",
          "target_interface": "CoreService.ListClusters",
          "operation_type": "grpc_unary",
          "conditions": [
            "c.ShouldBindQuery(params) succeeded"
          ],
          "operations": [
            {
              "operation": "ListClusters",
              "trigger": "Load the requested cluster from core-svc by cluster ID",
              "conditions": [
                "Always after query binding succeeds"
              ],
              "implementation": "grpc.GetClusterByID(ctx, params.ClusterID) -> client.ListClusters(ctx, req)"
            }
          ]
        }
      ]
    },
    "CentralAPI.CheckAPIKeyPermission": {
      "infrastructure": {},
      "services": [
        {
          "name": "account-provider",
          "target_repo": "tidbcloud/account",
          "target_interface": "AccountAPIService.HasPermissionForIdentity",
          "operation_type": "grpc_unary",
          "conditions": [
            "c.ShouldBindJSON(params) succeeded"
          ],
          "operations": [
            {
              "operation": "HasPermissionForIdentity",
              "trigger": "Check whether the API key has the requested resource permission",
              "conditions": [
                "Always after body binding succeeds"
              ],
              "implementation": "grpc.CheckAPIKeyPermission(ctx, params.APIKey, params.ResourceURI)"
            }
          ]
        }
      ]
    },
    "CentralAPI.CheckUserPermission": {
      "infrastructure": {},
      "services": [
        {
          "name": "account-provider",
          "target_repo": "tidbcloud/account",
          "target_interface": "AccountAPIService.GetUserByToken / HasPermissionForIdentity",
          "operation_type": "grpc_unary",
          "conditions": [
            "c.ShouldBindJSON(params) succeeded"
          ],
          "operations": [
            {
              "operation": "GetUserByToken",
              "trigger": "Resolve a user token into a user ID before permission evaluation",
              "conditions": [
                "params.UserToken != \"\""
              ],
              "implementation": "grpc.GetUserByToken(c.Request.Context(), &account.GetUserByTokenRequest{Token: params.UserToken})"
            },
            {
              "operation": "HasPermissionForIdentity",
              "trigger": "Check whether the resolved user ID has the requested resource permission",
              "conditions": [
                "Always after body binding succeeds",
                "UserToken path must first resolve params.UserID"
              ],
              "implementation": "grpc.CheckUserPermission(ctx, params.UserID, params.ResourceURI)"
            }
          ]
        }
      ]
    },
    "MetricsAPI.Metrics": {
      "infrastructure": {},
      "services": []
    },
    "ChatBotService.ChatBot": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "req.Caller != \"\"",
            "len(req.Question) <= maxQuestionLength",
            "c.checkIfHackyQuestion(ctx, req) did not short-circuit with an error"
          ],
          "operations": [
            {
              "operation": "INSERT",
              "trigger": "Increment the per-org daily rate-limit counter",
              "conditions": [
                "config.Global().ServerConf.EnableRateLimit == true"
              ],
              "implementation": "ratelimit.ByOrgIDPerDay(req.OrgId) -> dao.IncrRateLimit(identifier)"
            },
            {
              "operation": "SELECT",
              "trigger": "Validate an existing session against previously saved interaction records",
              "conditions": [
                "req.SessionId != \"\""
              ],
              "implementation": "dao.GetBotInteractRecordBySessionIDCreatedBy(req.SessionID, createdBy)"
            },
            {
              "operation": "INSERT",
              "trigger": "Persist a failed synchronous interaction attempt",
              "conditions": [
                "openaisvc.InteractBot returns err != nil",
                "req.Async == false"
              ],
              "implementation": "saveRequestRecord(ctx, req, prompt, completion, createdBy, errorMsg, executeTime) -> dao.CreateBotInteractRecord(record)"
            },
            {
              "operation": "INSERT",
              "trigger": "Persist the final stream result when stream mode is enabled",
              "conditions": [
                "config.Global().ServerConf.EnableStreamMode == true"
              ],
              "implementation": "requestOpenAIInStream(...) defer saveRequestRecord(ctx, req, prompt, completionResp.Completion, req.Caller, errMsg, endAt.Sub(startAt).Milliseconds())"
            }
          ]
        },
        "redis": {
          "conditions": [
            "config.Global().ServerConf.EnableStreamMode == true"
          ],
          "operations": [
            {
              "operation": "SETEX",
              "trigger": "Persist streaming progress and final completion state by job ID",
              "conditions": [
                "OpenAI stream emits chunks or finishes"
              ],
              "implementation": "postHandleStreamCompletion(...) -> cachesvc.SaveCompletionResult(ctx, jobID, completionResp)"
            }
          ]
        }
      },
      "services": [
        {
          "name": "dataservices",
          "target_repo": "tidbcloud/dataservices",
          "target_interface": "DataAPIService.GetClusterMeta / GetDatabaseMeta / ExecSqlDirectly",
          "operation_type": "grpc_unary",
          "conditions": [
            "req.Caller != \"\"",
            "Rate-limit and validation checks passed"
          ],
          "operations": [
            {
              "operation": "GetClusterMeta",
              "trigger": "Load cluster-wide metadata to guess the database when req.Database is empty for non-chat2sql flows",
              "conditions": [
                "req.Database == \"\"",
                "req.BotType != chat2sql"
              ],
              "implementation": "createCompletionForGuessDatabase(ctx, &guessDBReq) -> grpc.GetClusterMeta(ctx, req)"
            },
            {
              "operation": "GetDatabaseMeta",
              "trigger": "Load table metadata for database-scoped prompt generation when information-schema mode is disabled",
              "conditions": [
                "req.Database != \"\" or guessed database succeeded",
                "utils.GetInformationSchemaEnable(ctx, req.BotType) == false"
              ],
              "implementation": "getSqlEditorDatabaseColumns(ctx, req) -> grpc.GetDatabaseMeta(ctx, metaReq)"
            },
            {
              "operation": "ExecSqlDirectly",
              "trigger": "Load detailed schema metadata or run SQL dry-run validation through dataservices",
              "conditions": [
                "utils.GetInformationSchemaEnable(ctx, req.BotType) == true for metadata fetch",
                "validateSQLWithDryRun is reached when the generated SQL contains GROUP BY and sql_mode relaxation is enabled"
              ],
              "implementation": "getDatabaseDetailedMeta(ctx, req) / validateSQLWithDryRun(ctx, req, sql) -> grpc.GetDatabaseDetailedMeta(ctx, metaReq) / grpc.ValidateSQLWithDryRun(ctx, metaReq)"
            }
          ]
        },
        {
          "name": "openai",
          "target_repo": "api.openai.com",
          "target_interface": "CreateChatCompletion / CreateChatCompletionStream / CreateCompletion / CreateCompletionStream",
          "operation_type": "http_post",
          "conditions": [
            "req.Caller != \"\"",
            "Rate-limit and validation checks passed",
            "prompt generation succeeded"
          ],
          "operations": [
            {
              "operation": "CreateChatCompletionStream / CreateCompletionStream",
              "trigger": "Generate the response in stream mode and progressively persist it by job ID",
              "conditions": [
                "config.Global().ServerConf.EnableStreamMode == true"
              ],
              "implementation": "RequestOpenAIInStream(ctx, req, prompt, params, jobID, timeout) -> requestOpenAIInStreamWithGPT35(...) / requestOpenAIInStreamWithDavinci(...)"
            },
            {
              "operation": "CreateChatCompletion / CreateCompletion",
              "trigger": "Generate the response synchronously when stream mode is disabled",
              "conditions": [
                "config.Global().ServerConf.EnableStreamMode == false"
              ],
              "implementation": "requestOpenAI(ctx, req, prompt, params) -> openAIClient.CreateChatCompletion(ctx, openAIReq) / openAIClient.CreateCompletion(ctx, openAIReq)"
            }
          ]
        }
      ]
    },
    "ChatBotService.GetLatestPrompt": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Always"
          ],
          "operations": [
            {
              "operation": "SELECT",
              "trigger": "Load the most recent prompt template for the requested AI model and deployment",
              "conditions": [
                "Always"
              ],
              "implementation": "dao.GetLatestPromptByAIModelAndDeploy(req.AiModel, req.Deployment)"
            }
          ]
        }
      },
      "services": []
    },
    "ChatBotService.GetAllPromptByAIModel": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Always"
          ],
          "operations": [
            {
              "operation": "SELECT",
              "trigger": "Load all stored prompt templates that match the requested AI model and deployment",
              "conditions": [
                "Always"
              ],
              "implementation": "dao.GetAllPromptsByAIModelAndDeploy(req.AiModel, req.Deployment)"
            },
            {
              "operation": "SELECT",
              "trigger": "Resolve the latest prompt version for each AI-model plus deployment pair",
              "conditions": [
                "Once for each unique ai_model/deployment pair in the prompt result set"
              ],
              "implementation": "dao.GetLatestPromptByAIModelAndDeploy(prompt.AIModel, prompt.Deployment)"
            }
          ]
        }
      },
      "services": []
    },
    "ChatBotService.SavePrompt": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "req.Prompt != nil"
          ],
          "operations": [
            {
              "operation": "UPSERT",
              "trigger": "Persist the prompt template in the prompt_templates table",
              "conditions": [
                "Always after req.Prompt is parsed"
              ],
              "implementation": "dao.SavePrompt(req.Prompt.AiModel, req.Prompt.Deployment, req.Prompt.Version, req.Prompt.Content)"
            }
          ]
        }
      },
      "services": []
    },
    "ChatBotService.QueryCompletionByJobID": {
      "infrastructure": {
        "redis": {
          "conditions": [
            "req.JobId != \"\"",
            "streamEnabledAIModels[req.BotType] == true"
          ],
          "operations": [
            {
              "operation": "GET",
              "trigger": "Read the stored streamed completion payload by job ID",
              "conditions": [
                "Always after request validation succeeds"
              ],
              "implementation": "cachesvc.ReadCompletionResultByJobID(ctx, req.JobId)"
            }
          ]
        }
      },
      "services": []
    }
  }
}
