yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
learned_knowledge_service.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <chrono>
5#include <fstream>
6#include <iostream>
7#include <sstream>
8
9#include "absl/strings/str_cat.h"
10#include "absl/strings/str_format.h"
11#include "absl/time/clock.h"
12#include "absl/time/time.h"
13#include "util/platform_paths.h"
14
15namespace yaze {
16namespace cli {
17namespace agent {
18
19namespace {
20
22 return absl::ToUnixMillis(absl::Now());
23}
24
25std::string GenerateRandomID() {
26 static int counter = 0;
27 auto now = std::chrono::system_clock::now().time_since_epoch().count();
28 return absl::StrFormat("%lld_%d", now, counter++);
29}
30
31bool FileExists(const std::filesystem::path& path) {
32 return util::PlatformPaths::Exists(path);
33}
34
35} // namespace
36
38 // Get app data directory in a cross-platform way
39 auto app_data_result = util::PlatformPaths::GetAppDataSubdirectory("agent");
40 if (app_data_result.ok()) {
41 data_dir_ = *app_data_result;
42 } else {
43 // Fallback to current directory
44 data_dir_ = std::filesystem::current_path() / ".yaze" / "agent";
45 }
46
47 prefs_file_ = data_dir_ / "preferences.json";
48 patterns_file_ = data_dir_ / "patterns.json";
49 projects_file_ = data_dir_ / "projects.json";
50 memories_file_ = data_dir_ / "memories.json";
51}
52
54 const std::filesystem::path& data_dir)
55 : data_dir_(data_dir),
56 prefs_file_(data_dir / "preferences.json"),
57 patterns_file_(data_dir / "patterns.json"),
58 projects_file_(data_dir / "projects.json"),
59 memories_file_(data_dir / "memories.json") {}
60
62 if (initialized_) {
63 return absl::OkStatus();
64 }
65
66 // Ensure data directory exists
68 if (!status.ok()) {
69 return status;
70 }
71
72 // Load existing data
73 LoadPreferences(); // Ignore errors for empty files
77
78 initialized_ = true;
79 return absl::OkStatus();
80}
81
83 auto status = SavePreferences();
84 if (!status.ok()) return status;
85
86 status = SavePatterns();
87 if (!status.ok()) return status;
88
89 status = SaveProjects();
90 if (!status.ok()) return status;
91
92 status = SaveMemories();
93 if (!status.ok()) return status;
94
95 return absl::OkStatus();
96}
97
98// === Preference Management ===
99
100absl::Status LearnedKnowledgeService::SetPreference(const std::string& key,
101 const std::string& value) {
102 if (!initialized_) {
103 return absl::FailedPreconditionError("Service not initialized");
104 }
105
106 preferences_[key] = value;
107 return SavePreferences();
108}
109
110std::optional<std::string> LearnedKnowledgeService::GetPreference(
111 const std::string& key) const {
112 auto it = preferences_.find(key);
113 if (it != preferences_.end()) {
114 return it->second;
115 }
116 return std::nullopt;
117}
118
119std::map<std::string, std::string> LearnedKnowledgeService::GetAllPreferences() const {
120 return preferences_;
121}
122
123absl::Status LearnedKnowledgeService::RemovePreference(const std::string& key) {
124 if (!initialized_) {
125 return absl::FailedPreconditionError("Service not initialized");
126 }
127
128 preferences_.erase(key);
129 return SavePreferences();
130}
131
132// === ROM Pattern Learning ===
133
134absl::Status LearnedKnowledgeService::LearnPattern(const std::string& type,
135 const std::string& rom_hash,
136 const std::string& data,
137 float confidence) {
138 if (!initialized_) {
139 return absl::FailedPreconditionError("Service not initialized");
140 }
141
142 ROMPattern pattern;
143 pattern.pattern_type = type;
144 pattern.rom_hash = rom_hash;
145 pattern.pattern_data = data;
146 pattern.confidence = confidence;
147 pattern.learned_at = CurrentTimestamp();
148 pattern.access_count = 1;
149
150 patterns_.push_back(pattern);
151 return SavePatterns();
152}
153
154std::vector<LearnedKnowledgeService::ROMPattern>
156 const std::string& rom_hash) const {
157 std::vector<ROMPattern> results;
158
159 for (const auto& pattern : patterns_) {
160 bool type_match = type.empty() || pattern.pattern_type == type;
161 bool hash_match = rom_hash.empty() || pattern.rom_hash == rom_hash;
162
163 if (type_match && hash_match) {
164 results.push_back(pattern);
165 }
166 }
167
168 return results;
169}
170
172 const std::string& type,
173 const std::string& rom_hash,
174 float new_confidence) {
175 bool found = false;
176
177 for (auto& pattern : patterns_) {
178 if (pattern.pattern_type == type && pattern.rom_hash == rom_hash) {
179 pattern.confidence = new_confidence;
180 pattern.access_count++;
181 found = true;
182 }
183 }
184
185 if (!found) {
186 return absl::NotFoundError("Pattern not found");
187 }
188
189 return SavePatterns();
190}
191
192// === Project Context ===
193
195 const std::string& project_name,
196 const std::string& rom_hash,
197 const std::string& context) {
198 if (!initialized_) {
199 return absl::FailedPreconditionError("Service not initialized");
200 }
201
202 // Update existing or create new
203 bool found = false;
204 for (auto& project : projects_) {
205 if (project.project_name == project_name) {
206 project.rom_hash = rom_hash;
207 project.context_data = context;
208 project.last_accessed = CurrentTimestamp();
209 found = true;
210 break;
211 }
212 }
213
214 if (!found) {
215 ProjectContext project;
216 project.project_name = project_name;
217 project.rom_hash = rom_hash;
218 project.context_data = context;
219 project.last_accessed = CurrentTimestamp();
220 projects_.push_back(project);
221 }
222
223 return SaveProjects();
224}
225
226std::optional<LearnedKnowledgeService::ProjectContext>
227LearnedKnowledgeService::GetProjectContext(const std::string& project_name) const {
228 for (const auto& project : projects_) {
229 if (project.project_name == project_name) {
230 return project;
231 }
232 }
233 return std::nullopt;
234}
235
236std::vector<LearnedKnowledgeService::ProjectContext>
240
241// === Conversation Memory ===
242
244 const std::string& topic,
245 const std::string& summary,
246 const std::vector<std::string>& key_facts) {
247 if (!initialized_) {
248 return absl::FailedPreconditionError("Service not initialized");
249 }
250
251 ConversationMemory memory;
252 memory.id = GenerateRandomID();
253 memory.topic = topic;
254 memory.summary = summary;
255 memory.key_facts = key_facts;
256 memory.created_at = CurrentTimestamp();
257 memory.access_count = 1;
258
259 memories_.push_back(memory);
260
261 // Keep only last 100 memories
262 if (memories_.size() > 100) {
263 memories_.erase(memories_.begin());
264 }
265
266 return SaveMemories();
267}
268
269std::vector<LearnedKnowledgeService::ConversationMemory>
270LearnedKnowledgeService::SearchMemories(const std::string& query) const {
271 std::vector<ConversationMemory> results;
272
273 std::string query_lower = query;
274 std::transform(query_lower.begin(), query_lower.end(), query_lower.begin(), ::tolower);
275
276 for (const auto& memory : memories_) {
277 std::string topic_lower = memory.topic;
278 std::string summary_lower = memory.summary;
279 std::transform(topic_lower.begin(), topic_lower.end(), topic_lower.begin(), ::tolower);
280 std::transform(summary_lower.begin(), summary_lower.end(), summary_lower.begin(), ::tolower);
281
282 if (topic_lower.find(query_lower) != std::string::npos ||
283 summary_lower.find(query_lower) != std::string::npos) {
284 results.push_back(memory);
285 }
286 }
287
288 return results;
289}
290
291std::vector<LearnedKnowledgeService::ConversationMemory>
293 std::vector<ConversationMemory> recent = memories_;
294
295 // Sort by created_at descending
296 std::sort(recent.begin(), recent.end(),
297 [](const ConversationMemory& a, const ConversationMemory& b) {
298 return a.created_at > b.created_at;
299 });
300
301 if (recent.size() > static_cast<size_t>(limit)) {
302 recent.resize(limit);
303 }
304
305 return recent;
306}
307
308// === Import/Export ===
309
310#ifdef YAZE_WITH_JSON
311absl::StatusOr<std::string> LearnedKnowledgeService::ExportToJSON() const {
312 nlohmann::json export_data;
313
314 // Export preferences
315 export_data["preferences"] = preferences_;
316
317 // Export patterns
318 export_data["patterns"] = nlohmann::json::array();
319 for (const auto& pattern : patterns_) {
320 nlohmann::json p;
321 p["type"] = pattern.pattern_type;
322 p["rom_hash"] = pattern.rom_hash;
323 p["data"] = pattern.pattern_data;
324 p["confidence"] = pattern.confidence;
325 p["learned_at"] = pattern.learned_at;
326 p["access_count"] = pattern.access_count;
327 export_data["patterns"].push_back(p);
328 }
329
330 // Export projects
331 export_data["projects"] = nlohmann::json::array();
332 for (const auto& project : projects_) {
333 nlohmann::json p;
334 p["name"] = project.project_name;
335 p["rom_hash"] = project.rom_hash;
336 p["context"] = project.context_data;
337 p["last_accessed"] = project.last_accessed;
338 export_data["projects"].push_back(p);
339 }
340
341 // Export memories
342 export_data["memories"] = nlohmann::json::array();
343 for (const auto& memory : memories_) {
344 nlohmann::json m;
345 m["id"] = memory.id;
346 m["topic"] = memory.topic;
347 m["summary"] = memory.summary;
348 m["key_facts"] = memory.key_facts;
349 m["created_at"] = memory.created_at;
350 m["access_count"] = memory.access_count;
351 export_data["memories"].push_back(m);
352 }
353
354 return export_data.dump(2);
355}
356
357absl::Status LearnedKnowledgeService::ImportFromJSON(const std::string& json_data) {
358 try {
359 auto data = nlohmann::json::parse(json_data);
360
361 // Import preferences
362 if (data.contains("preferences")) {
363 for (const auto& [key, value] : data["preferences"].items()) {
364 preferences_[key] = value.get<std::string>();
365 }
366 }
367
368 // Import patterns
369 if (data.contains("patterns")) {
370 for (const auto& p : data["patterns"]) {
371 ROMPattern pattern;
372 pattern.pattern_type = p["type"];
373 pattern.rom_hash = p["rom_hash"];
374 pattern.pattern_data = p["data"];
375 pattern.confidence = p["confidence"];
376 pattern.learned_at = p["learned_at"];
377 pattern.access_count = p["access_count"];
378 patterns_.push_back(pattern);
379 }
380 }
381
382 // Import projects
383 if (data.contains("projects")) {
384 for (const auto& p : data["projects"]) {
385 ProjectContext project;
386 project.project_name = p["name"];
387 project.rom_hash = p["rom_hash"];
388 project.context_data = p["context"];
389 project.last_accessed = p["last_accessed"];
390 projects_.push_back(project);
391 }
392 }
393
394 // Import memories
395 if (data.contains("memories")) {
396 for (const auto& m : data["memories"]) {
397 ConversationMemory memory;
398 memory.id = m["id"];
399 memory.topic = m["topic"];
400 memory.summary = m["summary"];
401 memory.key_facts = m["key_facts"].get<std::vector<std::string>>();
402 memory.created_at = m["created_at"];
403 memory.access_count = m["access_count"];
404 memories_.push_back(memory);
405 }
406 }
407
408 return SaveAll();
409 } catch (const std::exception& e) {
410 return absl::InternalError(absl::StrCat("JSON parse error: ", e.what()));
411 }
412}
413#else
414absl::StatusOr<std::string> LearnedKnowledgeService::ExportToJSON() const {
415 return absl::UnimplementedError("JSON support not enabled. Build with -DYAZE_WITH_JSON=ON");
416}
417
418absl::Status LearnedKnowledgeService::ImportFromJSON(const std::string&) {
419 return absl::UnimplementedError("JSON support not enabled. Build with -DYAZE_WITH_JSON=ON");
420}
421#endif
422
424 preferences_.clear();
425 patterns_.clear();
426 projects_.clear();
427 memories_.clear();
428 return SaveAll();
429}
430
431// === Statistics ===
432
434 Stats stats;
435 stats.preference_count = preferences_.size();
436 stats.pattern_count = patterns_.size();
437 stats.project_count = projects_.size();
438 stats.memory_count = memories_.size();
439
440 // Find earliest learned_at
441 int64_t earliest = CurrentTimestamp();
442 for (const auto& pattern : patterns_) {
443 if (pattern.learned_at < earliest) {
444 earliest = pattern.learned_at;
445 }
446 }
447 for (const auto& memory : memories_) {
448 if (memory.created_at < earliest) {
449 earliest = memory.created_at;
450 }
451 }
452 stats.first_learned_at = earliest;
453
454 // Last updated is now
455 stats.last_updated_at = CurrentTimestamp();
456
457 return stats;
458}
459
460// === Internal Helpers ===
461
462#ifdef YAZE_WITH_JSON
464 if (!FileExists(prefs_file_)) {
465 return absl::OkStatus(); // No file yet, empty preferences
466 }
467
468 try {
469 std::ifstream file(prefs_file_);
470 if (!file.is_open()) {
471 return absl::InternalError("Failed to open preferences file");
472 }
473
474 nlohmann::json data;
475 file >> data;
476
477 for (const auto& [key, value] : data.items()) {
478 preferences_[key] = value.get<std::string>();
479 }
480
481 return absl::OkStatus();
482 } catch (const std::exception& e) {
483 return absl::InternalError(absl::StrCat("Failed to load preferences: ", e.what()));
484 }
485}
486
488 try {
489 nlohmann::json data = preferences_;
490
491 std::ofstream file(prefs_file_);
492 if (!file.is_open()) {
493 return absl::InternalError("Failed to open preferences file for writing");
494 }
495
496 file << data.dump(2);
497 return absl::OkStatus();
498 } catch (const std::exception& e) {
499 return absl::InternalError(absl::StrCat("Failed to save preferences: ", e.what()));
500 }
501}
502
505 return absl::OkStatus();
506 }
507
508 try {
509 std::ifstream file(patterns_file_);
510 nlohmann::json data;
511 file >> data;
512
513 for (const auto& p : data) {
514 ROMPattern pattern;
515 pattern.pattern_type = p["type"];
516 pattern.rom_hash = p["rom_hash"];
517 pattern.pattern_data = p["data"];
518 pattern.confidence = p["confidence"];
519 pattern.learned_at = p["learned_at"];
520 pattern.access_count = p.value("access_count", 0);
521 patterns_.push_back(pattern);
522 }
523
524 return absl::OkStatus();
525 } catch (const std::exception& e) {
526 return absl::InternalError(absl::StrCat("Failed to load patterns: ", e.what()));
527 }
528}
529
531 try {
532 nlohmann::json data = nlohmann::json::array();
533
534 for (const auto& pattern : patterns_) {
535 nlohmann::json p;
536 p["type"] = pattern.pattern_type;
537 p["rom_hash"] = pattern.rom_hash;
538 p["data"] = pattern.pattern_data;
539 p["confidence"] = pattern.confidence;
540 p["learned_at"] = pattern.learned_at;
541 p["access_count"] = pattern.access_count;
542 data.push_back(p);
543 }
544
545 std::ofstream file(patterns_file_);
546 file << data.dump(2);
547 return absl::OkStatus();
548 } catch (const std::exception& e) {
549 return absl::InternalError(absl::StrCat("Failed to save patterns: ", e.what()));
550 }
551}
552
555 return absl::OkStatus();
556 }
557
558 try {
559 std::ifstream file(projects_file_);
560 nlohmann::json data;
561 file >> data;
562
563 for (const auto& p : data) {
564 ProjectContext project;
565 project.project_name = p["name"];
566 project.rom_hash = p["rom_hash"];
567 project.context_data = p["context"];
568 project.last_accessed = p["last_accessed"];
569 projects_.push_back(project);
570 }
571
572 return absl::OkStatus();
573 } catch (const std::exception& e) {
574 return absl::InternalError(absl::StrCat("Failed to load projects: ", e.what()));
575 }
576}
577
579 try {
580 nlohmann::json data = nlohmann::json::array();
581
582 for (const auto& project : projects_) {
583 nlohmann::json p;
584 p["name"] = project.project_name;
585 p["rom_hash"] = project.rom_hash;
586 p["context"] = project.context_data;
587 p["last_accessed"] = project.last_accessed;
588 data.push_back(p);
589 }
590
591 std::ofstream file(projects_file_);
592 file << data.dump(2);
593 return absl::OkStatus();
594 } catch (const std::exception& e) {
595 return absl::InternalError(absl::StrCat("Failed to save projects: ", e.what()));
596 }
597}
598
600 try {
601 nlohmann::json data = nlohmann::json::array();
602
603 for (const auto& memory : memories_) {
604 nlohmann::json m;
605 m["id"] = memory.id;
606 m["topic"] = memory.topic;
607 m["summary"] = memory.summary;
608 m["key_facts"] = memory.key_facts;
609 m["created_at"] = memory.created_at;
610 m["access_count"] = memory.access_count;
611 data.push_back(m);
612 }
613
614 std::ofstream file(memories_file_);
615 file << data.dump(2);
616 return absl::OkStatus();
617 } catch (const std::exception& e) {
618 return absl::InternalError(absl::StrCat("Failed to save memories: ", e.what()));
619 }
620}
621
624 return absl::OkStatus();
625 }
626
627 try {
628 std::ifstream file(memories_file_);
629 nlohmann::json data;
630 file >> data;
631
632 for (const auto& m : data) {
633 ConversationMemory memory;
634 memory.id = m["id"];
635 memory.topic = m["topic"];
636 memory.summary = m["summary"];
637 memory.key_facts = m["key_facts"].get<std::vector<std::string>>();
638 memory.created_at = m["created_at"];
639 memory.access_count = m.value("access_count", 0);
640 memories_.push_back(memory);
641 }
642
643 return absl::OkStatus();
644 } catch (const std::exception& e) {
645 return absl::InternalError(absl::StrCat("Failed to load memories: ", e.what()));
646 }
647}
648
649#else
650// Stub implementations when JSON is not available
651absl::Status LearnedKnowledgeService::LoadPreferences() { return absl::OkStatus(); }
652absl::Status LearnedKnowledgeService::SavePreferences() { return absl::UnimplementedError("JSON support required"); }
653absl::Status LearnedKnowledgeService::LoadPatterns() { return absl::OkStatus(); }
654absl::Status LearnedKnowledgeService::SavePatterns() { return absl::UnimplementedError("JSON support required"); }
655absl::Status LearnedKnowledgeService::LoadProjects() { return absl::OkStatus(); }
656absl::Status LearnedKnowledgeService::SaveProjects() { return absl::UnimplementedError("JSON support required"); }
657absl::Status LearnedKnowledgeService::LoadMemories() { return absl::OkStatus(); }
658absl::Status LearnedKnowledgeService::SaveMemories() { return absl::UnimplementedError("JSON support required"); }
659#endif
660
662 return GenerateRandomID();
663}
664
665} // namespace agent
666} // namespace cli
667} // namespace yaze
std::optional< std::string > GetPreference(const std::string &key) const
absl::Status StoreConversationSummary(const std::string &topic, const std::string &summary, const std::vector< std::string > &key_facts)
std::vector< ProjectContext > GetAllProjects() const
std::vector< ConversationMemory > GetRecentMemories(int limit=10) const
absl::Status RemovePreference(const std::string &key)
std::vector< ConversationMemory > SearchMemories(const std::string &query) const
std::vector< ROMPattern > QueryPatterns(const std::string &type, const std::string &rom_hash="") const
absl::Status ImportFromJSON(const std::string &json_data)
absl::Status UpdatePatternConfidence(const std::string &type, const std::string &rom_hash, float new_confidence)
std::map< std::string, std::string > preferences_
std::optional< ProjectContext > GetProjectContext(const std::string &project_name) const
absl::Status SetPreference(const std::string &key, const std::string &value)
absl::StatusOr< std::string > ExportToJSON() const
absl::Status SaveProjectContext(const std::string &project_name, const std::string &rom_hash, const std::string &context)
absl::Status LearnPattern(const std::string &type, const std::string &rom_hash, const std::string &data, float confidence=1.0f)
std::map< std::string, std::string > GetAllPreferences() const
static absl::StatusOr< std::filesystem::path > GetAppDataSubdirectory(const std::string &subdir)
Get a subdirectory within the app data folder.
static absl::Status EnsureDirectoryExists(const std::filesystem::path &path)
Ensure a directory exists, creating it if necessary.
static bool Exists(const std::filesystem::path &path)
Check if a file or directory exists.
Main namespace for the application.