forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathknowledge_graph_api.dart
More file actions
94 lines (79 loc) · 3.31 KB
/
Copy pathknowledge_graph_api.dart
File metadata and controls
94 lines (79 loc) · 3.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:omi/backend/http/shared.dart';
import 'package:omi/backend/schema/gen/misc_wire.g.dart' as wire;
import 'package:omi/env/env.dart';
import 'package:omi/utils/logger.dart';
class KnowledgeGraphApi {
static final String _baseUrl = '${Env.apiBaseUrl}v1/knowledge-graph';
/// Short user-facing copy for a knowledge-graph HTTP failure.
///
/// [statusCode] and [body] are accepted so a regression that interpolates
/// them into the returned string is caught by tests. They belong in debug
/// logs only.
@visibleForTesting
static String knowledgeGraphHttpUserMessage({required String action, int? statusCode, String? body}) {
final _ = (statusCode, body);
return "Couldn't $action knowledge graph";
}
static Never _throwHttpFailure({required String action, int? statusCode, String? body}) {
Logger.debug('Failed to $action knowledge graph: status=$statusCode body=$body');
throw Exception(knowledgeGraphHttpUserMessage(action: action, statusCode: statusCode, body: body));
}
static Future<Map<String, dynamic>> getKnowledgeGraph() async {
final response = await makeApiCall(
url: '${Env.apiBaseUrl}v1/knowledge-graph',
headers: {},
body: '',
method: 'GET',
timeout: const Duration(seconds: 60),
retries: 0,
);
if (response != null && response.statusCode == 200) {
return wire.GeneratedKnowledgeGraphResponse.fromJson(jsonDecode(response.body) as Map<String, dynamic>).toJson();
}
_throwHttpFailure(action: 'load', statusCode: response?.statusCode, body: response?.body);
}
static Future<Map<String, dynamic>> rebuildKnowledgeGraph() async {
final response = await makeApiCall(url: '$_baseUrl/rebuild', headers: {}, body: '{}', method: 'POST');
if (response != null && response.statusCode == 200) {
return wire.GeneratedRebuildResponse.fromJson(jsonDecode(response.body) as Map<String, dynamic>).toJson();
}
_throwHttpFailure(action: 'rebuild', statusCode: response?.statusCode, body: response?.body);
}
/// Polls the graph endpoint until the node count stabilizes or timeout is reached.
/// Returns the final graph data.
static Future<Map<String, dynamic>> waitForGraphStability({
Duration timeout = const Duration(seconds: 45),
Duration interval = const Duration(seconds: 2),
int stabilityChecks = 2,
}) async {
int stableCount = 0;
int lastCount = -1;
final stopwatch = Stopwatch()..start();
while (stopwatch.elapsed < timeout) {
try {
await Future.delayed(interval);
final data = await getKnowledgeGraph();
final nodes = data['nodes'] as List<dynamic>? ?? [];
final count = nodes.length;
// Reset stability count if node count changes
if (count > 0 && count == lastCount) {
stableCount++;
} else {
stableCount = 0;
}
lastCount = count;
// If stable for [stabilityChecks] cycles and we have data, return it
if (stableCount >= stabilityChecks && count > 0) {
return data;
}
} catch (e) {
// Silently ignore temporary fetch errors during polling
print('Polling error: $e');
}
}
// Return whatever we have at timeout
return await getKnowledgeGraph();
}
}