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