47 return absl::UnimplementedError(
48 "Ollama service requires JSON support. "
49 "Build with -DZ3ED_AI=ON or -DYAZE_WITH_JSON=ON");
53 cli.set_connection_timeout(5);
55 auto res = cli.Get(
"/api/tags");
57 return absl::UnavailableError(absl::StrFormat(
58 "Cannot connect to Ollama server at %s.\n"
59 "Make sure Ollama is installed and running:\n"
60 " 1. Install: brew install ollama (macOS) or https://ollama.com/download\n"
61 " 2. Start: ollama serve\n"
62 " 3. Verify: curl http://localhost:11434/api/tags",
66 if (res->status != 200) {
67 return absl::InternalError(absl::StrFormat(
68 "Ollama server error: HTTP %d\nResponse: %s",
69 res->status, res->body));
73 nlohmann::json models_json = nlohmann::json::parse(res->body);
74 bool model_found =
false;
76 if (models_json.contains(
"models") && models_json[
"models"].is_array()) {
77 for (
const auto& model : models_json[
"models"]) {
78 if (model.contains(
"name")) {
79 std::string model_name = model[
"name"].get<std::string>();
80 if (model_name.find(
config_.
model) != std::string::npos) {
89 return absl::NotFoundError(absl::StrFormat(
90 "Model '%s' not found on Ollama server.\n"
91 "Pull it with: ollama pull %s\n"
92 "Available models: ollama list",
96 return absl::OkStatus();
97 }
catch (
const std::exception& e) {
98 return absl::InternalError(absl::StrCat(
99 "Ollama health check failed: ", e.what()));
105#ifndef YAZE_WITH_JSON
106 return absl::UnimplementedError(
"Requires httplib and JSON support");
110 cli.set_connection_timeout(5);
112 auto res = cli.Get(
"/api/tags");
114 if (!res || res->status != 200) {
115 return absl::UnavailableError(
116 "Cannot list Ollama models. Is the server running?");
119 nlohmann::json models_json = nlohmann::json::parse(res->body);
120 std::vector<std::string> models;
122 if (models_json.contains(
"models") && models_json[
"models"].is_array()) {
123 for (
const auto& model : models_json[
"models"]) {
124 if (model.contains(
"name")) {
125 models.push_back(model[
"name"].get<std::string>());
131 }
catch (
const std::exception& e) {
132 return absl::InternalError(absl::StrCat(
133 "Failed to list models: ", e.what()));
139 const std::string& json_response) {
141 return absl::UnimplementedError(
"Requires JSON support");
144 nlohmann::json response_json = nlohmann::json::parse(json_response);
146 if (!response_json.contains(
"response")) {
147 return absl::InvalidArgumentError(
148 "Ollama response missing 'response' field");
151 return response_json[
"response"].get<std::string>();
152 }
catch (
const nlohmann::json::exception& e) {
153 return absl::InternalError(absl::StrCat(
154 "Failed to parse Ollama response: ", e.what()));
165 const std::vector<agent::ChatMessage>& history) {
166#ifndef YAZE_WITH_JSON
167 return absl::UnimplementedError(
168 "Ollama service requires httplib and JSON support. "
169 "Install vcpkg dependencies or use bundled libraries.");
172 if (history.empty()) {
173 return absl::InvalidArgumentError(
"History cannot be empty.");
178 nlohmann::json request_body = {
191 cli.set_read_timeout(60);
193 auto res = cli.Post(
"/api/generate", request_body.dump(),
"application/json");
196 return absl::UnavailableError(
197 "Failed to connect to Ollama. Is 'ollama serve' running?\n"
198 "Start with: ollama serve");
201 if (res->status != 200) {
202 return absl::InternalError(absl::StrFormat(
203 "Ollama API error: HTTP %d\nResponse: %s",
204 res->status, res->body));
208 nlohmann::json ollama_wrapper;
210 ollama_wrapper = nlohmann::json::parse(res->body);
211 }
catch (
const nlohmann::json::exception& e) {
212 return absl::InternalError(absl::StrFormat(
213 "Failed to parse Ollama response: %s\nBody: %s",
214 e.what(), res->body));
218 if (!ollama_wrapper.contains(
"response") || !ollama_wrapper[
"response"].is_string()) {
219 return absl::InvalidArgumentError(
220 "Ollama response missing 'response' field");
223 std::string llm_output = ollama_wrapper[
"response"].get<std::string>();
226 const char* verbose_env = std::getenv(
"Z3ED_VERBOSE");
227 if (verbose_env && std::string(verbose_env) ==
"1") {
228 std::cout <<
"\n" <<
"\033[35m" <<
"🔍 Raw LLM Response:" <<
"\033[0m" <<
"\n"
229 <<
"\033[2m" << llm_output <<
"\033[0m" <<
"\n\n";
233 nlohmann::json response_json;
235 response_json = nlohmann::json::parse(llm_output);
236 }
catch (
const nlohmann::json::exception& e) {
238 size_t start = llm_output.find(
'{');
239 size_t end = llm_output.rfind(
'}');
241 if (start != std::string::npos && end != std::string::npos && end > start) {
242 std::string json_only = llm_output.substr(start, end - start + 1);
244 response_json = nlohmann::json::parse(json_only);
245 }
catch (
const nlohmann::json::exception&) {
246 return absl::InvalidArgumentError(
247 "LLM did not return valid JSON. Response:\n" + llm_output);
250 return absl::InvalidArgumentError(
251 "LLM did not return a JSON object. Response:\n" + llm_output);
256 if (response_json.contains(
"text_response") &&
257 response_json[
"text_response"].is_string()) {
259 response_json[
"text_response"].get<std::string>();
261 if (response_json.contains(
"reasoning") &&
262 response_json[
"reasoning"].is_string()) {
263 agent_response.
reasoning = response_json[
"reasoning"].get<std::string>();
265 if (response_json.contains(
"tool_calls") &&
266 response_json[
"tool_calls"].is_array()) {
267 for (
const auto& call : response_json[
"tool_calls"]) {
268 if (call.contains(
"tool_name") && call[
"tool_name"].is_string()) {
270 tool_call.
tool_name = call[
"tool_name"].get<std::string>();
271 if (call.contains(
"args") && call[
"args"].is_object()) {
272 for (
auto& [key, value] : call[
"args"].items()) {
273 if (value.is_string()) {
274 tool_call.
args[key] = value.get<std::string>();
278 agent_response.
tool_calls.push_back(tool_call);
282 if (response_json.contains(
"commands") &&
283 response_json[
"commands"].is_array()) {
284 for (
const auto& cmd : response_json[
"commands"]) {
285 if (cmd.is_string()) {
286 agent_response.
commands.push_back(cmd.get<std::string>());
291 return agent_response;
293 }
catch (
const std::exception& e) {
294 return absl::InternalError(
295 absl::StrCat(
"Ollama request failed: ", e.what()));