1+ #include " ydb_ai.h"
2+
3+ #include < ydb/public/lib/ydb_cli/commands/ydb_ai/common/json_utils.h>
4+ #include < ydb/public/lib/ydb_cli/commands/ydb_ai/line_reader.h>
5+ #include < ydb/public/lib/ydb_cli/commands/ydb_ai/models/model_anthropic.h>
6+ #include < ydb/public/lib/ydb_cli/commands/ydb_ai/models/model_openai.h>
7+ #include < ydb/public/lib/ydb_cli/commands/ydb_ai/tools/exec_query_tool.h>
8+ #include < ydb/public/lib/ydb_cli/commands/ydb_ai/tools/list_directory_tool.h>
9+
10+ #include < util/string/strip.h>
11+ #include < util/system/env.h>
12+
13+ /*
14+
15+ FEATURES-TODO:
16+
17+ - Streamable model response printing
18+ - Streamable results printing
19+ - Adjusting errors, progress and response printing
20+ - Approving before tool use
21+ - Integration into common interactive mode
22+ - Think about helps
23+ - Think about robust
24+ - Provide system promt
25+ - Somehow render markdown
26+
27+ */
28+
29+ namespace NYdb ::NConsoleClient {
30+
31+ namespace {
32+
33+ void PrintExitMessage () {
34+ Cout << " \n Bye" << Endl;
35+ }
36+
37+ } // anonymous namespace
38+
39+ TCommandAi::TCommandAi ()
40+ : TBase(" ai" , {}, " AI-TODO: KIKIMR-24198 -- description" )
41+ {}
42+
43+ void TCommandAi::Config (TConfig& config) {
44+ TBase::Config (config);
45+ config.Opts ->SetTitle (" AI-TODO: KIKIMR-24198 -- title" );
46+ config.Opts ->SetFreeArgsNum (0 );
47+ }
48+
49+ int TCommandAi::Run (TConfig& config) {
50+ Cout << " AI-TODO: KIKIMR-24198 -- welcome message" << Endl;
51+
52+ // AI-TODO: KIKIMR-24202 - robust file creation
53+ NAi::TLineReader lineReader (" ydb-ai> " , (TFsPath (HomeDir) / " .ydb-ai/history" ).GetPath ());
54+
55+ // DeepSeek
56+ // const auto model = NAi::CreateOpenAiModel({
57+ // .BaseUrl = "https://api.eliza.yandex.net/raw/internal/deepseek", // AI-TODO: KIKIMR-24214 -- configure it
58+ // .ModelId = "deepseek-0324", // AI-TODO: KIKIMR-24214 -- configure it
59+ // .ApiKey = GetEnv("MODEL_TOKEN"), // AI-TODO: KIKIMR-24214 -- configure it
60+ // }, config);
61+
62+ // Claude 3.5 haiku
63+ // const auto model = NAi::CreateAnthropicModel({
64+ // .BaseUrl = "https://api.eliza.yandex.net/anthropic", // AI-TODO: KIKIMR-24214 -- configure it
65+ // .ModelId = "claude-3-5-haiku-20241022",
66+ // .ApiKey = GetEnv("MODEL_TOKEN"), // AI-TODO: KIKIMR-24214 -- configure it
67+ // }, config);
68+
69+ // YandexGPT Pro
70+ const auto model = NAi::CreateOpenAiModel ({
71+ .BaseUrl = " https://api.eliza.yandex.net/internal/zeliboba/32b_aligned_quantized_202506/generative" , // AI-TODO: KIKIMR-24214 -- configure it
72+ .ApiKey = GetEnv (" MODEL_TOKEN" ), // AI-TODO: KIKIMR-24214 -- configure it
73+ }, config);
74+
75+ std::unordered_map<TString, NAi::ITool::TPtr> tools = {
76+ {" execute_query" , NAi::CreateExecQueryTool (config)},
77+ {" list_directory" , NAi::CreateListDirectoryTool (config)},
78+ };
79+ for (const auto & [name, tool] : tools) {
80+ model->RegisterTool (name, tool->GetParametersSchema (), tool->GetDescription ());
81+ }
82+
83+ // AI-TODO: there is strange highlighting of brackets
84+ std::vector<NAi::IModel::TMessage> messages;
85+ while (const auto & maybeLine = lineReader.ReadLine ()) {
86+ const auto & input = *maybeLine;
87+ if (input.empty ()) {
88+ continue ;
89+ }
90+
91+ if (IsIn ({" quit" , " exit" }, to_lower (input))) {
92+ PrintExitMessage ();
93+ return EXIT_SUCCESS;
94+ }
95+
96+ // AI-TODO: limit interaction number
97+ messages.emplace_back (NAi::IModel::TUserMessage{.Text = input});
98+ while (!messages.empty ()) {
99+ // AI-TODO: progress visualization
100+ const auto output = model->HandleMessages (messages);
101+ messages.clear ();
102+
103+ if (!output.Text && output.ToolCalls .empty ()) {
104+ // AI-TODO: proper answer format
105+ Cout << " Model answer is empty(" << Endl;
106+ break ;
107+ }
108+
109+ if (output.Text ) {
110+ // AI-TODO: proper answer format
111+ Cout << " Model answer:\n " << output.Text << Endl;
112+ }
113+
114+ for (const auto & toolCall : output.ToolCalls ) {
115+ const auto it = tools.find (toolCall.Name );
116+ if (it == tools.end ()) {
117+ // AI-TODO: proper wrong tool handling
118+ Cout << " Unsupported tool: " << toolCall.Name << Endl;
119+ return EXIT_FAILURE;
120+ }
121+
122+ // AI-TODO: proper tool call printing
123+ Cout << " Calling tool: " << toolCall.Name << " with params:\n " << NAi::FormatJsonValue (toolCall.Parameters ) << Endl;
124+
125+ // AI-TODO: add approving
126+ const auto & result = it->second ->Execute (toolCall.Parameters );
127+ if (!result.IsSuccess ) {
128+ // AI-TODO: proper error handling
129+ Cout << result.Text << Endl;
130+ }
131+ // AI-TODO: show progress
132+
133+ messages.push_back (NAi::IModel::TToolResponse{
134+ .Text = result.Text ,
135+ .ToolCallId = toolCall.Id ,
136+ .IsSuccess = result.IsSuccess ,
137+ });
138+ }
139+ }
140+ }
141+
142+ PrintExitMessage ();
143+ return EXIT_SUCCESS;
144+ }
145+
146+ } // namespace NYdb::NConsoleClient
0 commit comments