9#include "absl/flags/declare.h"
10#include "absl/flags/flag.h"
11#include "absl/strings/ascii.h"
12#include "absl/strings/match.h"
13#include "absl/strings/str_format.h"
35 provider = absl::AsciiStrToLower(provider);
58 if (rom_path.empty()) {
61 const std::string lowered = absl::AsciiStrToLower(std::string(rom_path));
62 return absl::StrContains(lowered,
"oracle") ||
63 absl::StrContains(lowered,
"oos");
69 if (!asset_path.ok()) {
72 std::ifstream file(asset_path->string());
76 return std::string(std::istreambuf_iterator<char>(file),
77 std::istreambuf_iterator<char>());
84 const char* env_ollama_host = std::getenv(
"OLLAMA_HOST");
85 if (env_ollama_host && *env_ollama_host) {
88 const char* env_ollama_model = std::getenv(
"OLLAMA_MODEL");
89 return env_ollama_model && *env_ollama_model;
99 if (
const char* env_key = std::getenv(
"GEMINI_API_KEY")) {
104 if (
const char* env_key = std::getenv(
"ANTHROPIC_API_KEY")) {
109 if (
const char* openai_key = std::getenv(
"OPENAI_API_KEY")) {
115 const char* env_openai_base = std::getenv(
"OPENAI_BASE_URL");
116 if (!env_openai_base || !*env_openai_base) {
117 env_openai_base = std::getenv(
"OPENAI_API_BASE");
119 if (env_openai_base && *env_openai_base) {
124 if (
const char* env_ollama_host = std::getenv(
"OLLAMA_HOST");
125 env_ollama_host && *env_ollama_host) {
129 if (config.
model.empty()) {
130 if (
const char* env_model = std::getenv(
"OLLAMA_MODEL")) {
131 config.
model = env_model;
156 std::unique_ptr<yaze::cli::AIService> service,
158 if (service !=
nullptr && config.
rom_context !=
nullptr) {
181 config.
provider = absl::GetFlag(FLAGS_ai_provider);
182 config.
model = absl::GetFlag(FLAGS_ai_model);
185 config.
ollama_host = absl::GetFlag(FLAGS_ollama_host);
188 ApplyEnvironmentFallbacks(config);
189 return NormalizeConfig(std::move(config));
206 std::vector<AIServiceConfig> configs;
207 std::set<std::string> seen_providers;
209 auto append_provider = [&](absl::string_view provider_name) {
210 const std::string canonical =
211 NormalizeProviderAlias(std::string(provider_name));
212 if (canonical.empty() || !seen_providers.insert(canonical).second) {
216 provider_config.
provider = canonical;
217 configs.push_back(std::move(provider_config));
221 append_provider(effective_config.
provider);
228 if (HasOpenAiEndpointHint(effective_config) ||
235 if (HasOllamaHint(effective_config)) {
250 std::cout <<
"🤖 Auto-detecting AI provider...\n";
251 std::cout <<
" Found Gemini API key, using Gemini\n";
253 }
else if (HasOpenAiEndpointHint(effective_config)) {
254 std::cout <<
"🤖 Auto-detecting AI provider...\n";
255 std::cout <<
" Found OpenAI-compatible base URL, using OpenAI\n";
256 if (effective_config.
model.empty()) {
257 std::cout <<
" Tip: Set --ai_model for local servers\n";
261 std::cout <<
"🤖 Auto-detecting AI provider...\n";
262 std::cout <<
" Found Anthropic API key, using Anthropic\n";
265 std::cout <<
"🤖 Auto-detecting AI provider...\n";
266 std::cout <<
" Found OpenAI API key, using OpenAI\n";
268 if (effective_config.
model.empty()) {
269 effective_config.
model =
"gpt-4o-mini";
271 }
else if (HasOllamaHint(effective_config)) {
272 std::cout <<
"🤖 Auto-detecting AI provider...\n";
273 std::cout <<
" Found Ollama configuration, using Ollama\n";
276 std::cout <<
"🤖 No AI provider configured, using MockAIService\n";
278 <<
" Tip: Set GEMINI_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY,"
279 " OPENAI_BASE_URL, or OLLAMA_HOST/OLLAMA_MODEL\n";
285 std::cout <<
"🤖 AI Provider: " << effective_config.
provider <<
"\n";
289 if (service_or.ok()) {
290 return std::move(service_or.value());
293 std::cerr <<
"⚠️ " << service_or.status().message() << std::endl;
294 std::cerr <<
" Falling back to MockAIService" << std::endl;
295 return FinalizeService(std::make_unique<MockAIService>(), effective_config);
301 const std::string provider = effective_config.
provider;
303 return absl::InvalidArgumentError(
304 "CreateAIServiceStrict requires an explicit provider (not 'auto')");
307 const std::string oracle_system_instruction =
308 ResolveOracleSystemInstruction(effective_config);
311 return FinalizeService(std::make_unique<MockAIService>(), effective_config);
317 if (!effective_config.
model.empty()) {
319 }
else if (
const char* env_model = std::getenv(
"OLLAMA_MODEL")) {
320 ollama_config.
model = env_model;
322 if (!oracle_system_instruction.empty()) {
325 return FinalizeService(std::make_unique<OllamaAIService>(ollama_config),
330 return FinalizeService(
331 std::make_unique<LocalGeminiCliService>(effective_config.
model.empty()
333 : effective_config.
model),
340 return absl::FailedPreconditionError(
341 "Gemini API key not provided. Set --gemini_api_key or "
345 if (!effective_config.
model.empty()) {
348 if (!oracle_system_instruction.empty()) {
351 gemini_config.
prompt_version = absl::GetFlag(FLAGS_prompt_version);
353 absl::GetFlag(FLAGS_use_function_calling);
355 return FinalizeService(std::make_unique<GeminiAIService>(gemini_config),
360 return absl::FailedPreconditionError(
361 "Anthropic API key not provided. Set --anthropic_api_key or "
362 "ANTHROPIC_API_KEY.");
365 if (!effective_config.
model.empty()) {
366 anthropic_config.
model = effective_config.
model;
368 if (!oracle_system_instruction.empty()) {
371 anthropic_config.
prompt_version = absl::GetFlag(FLAGS_prompt_version);
373 absl::GetFlag(FLAGS_use_function_calling);
375 return FinalizeService(
376 std::make_unique<AnthropicAIService>(anthropic_config),
380 const bool is_local_server =
382 if (effective_config.
openai_api_key.empty() && !is_local_server) {
383 return absl::FailedPreconditionError(
384 "OpenAI API key not provided. Set OPENAI_API_KEY.\n"
385 "For LMStudio, use --openai_base_url=http://localhost:1234");
389 if (!effective_config.
model.empty()) {
392 if (!oracle_system_instruction.empty()) {
395 openai_config.
prompt_version = absl::GetFlag(FLAGS_prompt_version);
397 absl::GetFlag(FLAGS_use_function_calling);
399 return FinalizeService(std::make_unique<OpenAIAIService>(openai_config),
404 return absl::FailedPreconditionError(
405 "AI support not available: rebuild with YAZE_WITH_JSON=ON");
409 return absl::InvalidArgumentError(
410 absl::StrFormat(
"Unknown AI provider: %s", config.
provider));
constexpr char kDefaultOpenAiBaseUrl[]
yaze::cli::AIServiceConfig NormalizeConfig(yaze::cli::AIServiceConfig config)
std::unique_ptr< yaze::cli::AIService > FinalizeService(std::unique_ptr< yaze::cli::AIService > service, const yaze::cli::AIServiceConfig &config)
bool HasOllamaHint(const yaze::cli::AIServiceConfig &config)
std::string NormalizeProviderAlias(std::string provider)
void ApplyEnvironmentFallbacks(yaze::cli::AIServiceConfig &config)
bool IsLikelyOracleRomPath(absl::string_view rom_path)
constexpr char kOraclePromptAsset[]
constexpr char kDefaultOllamaHost[]
std::string ResolveOracleSystemInstruction(const yaze::cli::AIServiceConfig &config)
bool HasOpenAiEndpointHint(const yaze::cli::AIServiceConfig &config)
std::string ReadAssetFile(absl::string_view relative_path)
constexpr char kProviderGemini[]
constexpr char kProviderGpt[]
constexpr char kProviderOpus[]
constexpr char kProviderGoogle[]
constexpr char kProviderCustomOpenAi[]
std::unique_ptr< AIService > CreateAIService()
AgentPromptProfile DetectPromptProfile(const AIServiceConfig &config)
constexpr char kProviderAnthropic[]
constexpr char kProviderGeminiCli[]
absl::StatusOr< std::unique_ptr< AIService > > CreateAIServiceStrict(const AIServiceConfig &config)
constexpr char kProviderAuto[]
constexpr char kProviderChatGpt[]
AIServiceConfig BuildAIServiceConfigFromFlags()
std::string NormalizeOpenAiBaseUrl(std::string base)
constexpr char kProviderGoogleGemini[]
constexpr char kProviderMock[]
constexpr char kProviderClaude[]
constexpr char kProviderLocalGemini[]
constexpr char kProviderAnthropicClaude[]
constexpr char kProviderOpenAiCompatible[]
std::vector< AIServiceConfig > DiscoverModelRegistryConfigs(const AIServiceConfig &base_config)
constexpr char kProviderLmStudioDashed[]
constexpr char kProviderOpenAi[]
constexpr char kProviderOllama[]
constexpr char kProviderSonnet[]
constexpr char kProviderLmStudio[]
ABSL_DECLARE_FLAG(std::string, ai_provider)
std::string rom_path_hint
std::string gemini_api_key
std::string openai_base_url
std::string anthropic_api_key
std::string openai_api_key
bool use_function_calling
std::string prompt_version
std::string system_instruction
bool use_function_calling
std::string prompt_version
std::string system_instruction
std::string system_prompt
bool use_function_calling
std::string prompt_version
std::string system_instruction