cortexdb-sdk 0.2.0b2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- _cortexdb_client/__init__.py +73 -0
- _cortexdb_client/answering.py +59 -0
- _cortexdb_client/aql.py +106 -0
- _cortexdb_client/client.py +291 -0
- _cortexdb_client/errors.py +34 -0
- _cortexdb_client/generated/__init__.py +3 -0
- _cortexdb_client/generated/openapi_types.py +1305 -0
- _cortexdb_client/grounding.py +197 -0
- _cortexdb_client/model_types/__init__.py +66 -0
- _cortexdb_client/model_types/context.py +163 -0
- _cortexdb_client/model_types/core.py +187 -0
- _cortexdb_client/model_types/ingestion.py +63 -0
- _cortexdb_client/model_types/memory.py +20 -0
- _cortexdb_client/model_types/search.py +165 -0
- _cortexdb_client/model_types/verification.py +88 -0
- _cortexdb_client/models.py +73 -0
- _cortexdb_client/py.typed +1 -0
- _cortexdb_client/transport.py +94 -0
- cortexdb_client.py +75 -0
- cortexdb_sdk-0.2.0b2.dist-info/METADATA +58 -0
- cortexdb_sdk-0.2.0b2.dist-info/RECORD +23 -0
- cortexdb_sdk-0.2.0b2.dist-info/WHEEL +5 -0
- cortexdb_sdk-0.2.0b2.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from .models import (
|
|
6
|
+
AnswerGroundingReportResponse,
|
|
7
|
+
AnswerGroundingSpanResponse,
|
|
8
|
+
ContextPackResponse,
|
|
9
|
+
GroundedAnswerResponse,
|
|
10
|
+
VerificationReportResponse,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _tokenize(text: str) -> tuple[str, ...]:
|
|
15
|
+
terms: list[str] = []
|
|
16
|
+
current: list[str] = []
|
|
17
|
+
for character in text.lower():
|
|
18
|
+
if character.isalnum():
|
|
19
|
+
current.append(character)
|
|
20
|
+
elif current:
|
|
21
|
+
term = "".join(current)
|
|
22
|
+
if term not in {"a", "an", "and", "the", "or", "of", "to", "in"}:
|
|
23
|
+
terms.append(term)
|
|
24
|
+
current = []
|
|
25
|
+
if current:
|
|
26
|
+
term = "".join(current)
|
|
27
|
+
if term not in {"a", "an", "and", "the", "or", "of", "to", "in"}:
|
|
28
|
+
terms.append(term)
|
|
29
|
+
return tuple(sorted(set(terms)))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _split_answer_spans(answer: str) -> tuple[tuple[str, int, int], ...]:
|
|
33
|
+
spans: list[tuple[str, int, int]] = []
|
|
34
|
+
start = 0
|
|
35
|
+
for index, character in enumerate(answer):
|
|
36
|
+
if character in {"!", "?", "\n"} or (
|
|
37
|
+
character == "."
|
|
38
|
+
and not (
|
|
39
|
+
index > 0
|
|
40
|
+
and index + 1 < len(answer)
|
|
41
|
+
and answer[index - 1].isdigit()
|
|
42
|
+
and answer[index + 1].isdigit()
|
|
43
|
+
)
|
|
44
|
+
):
|
|
45
|
+
_push_answer_span(answer, start, index + 1, spans)
|
|
46
|
+
start = index + 1
|
|
47
|
+
_push_answer_span(answer, start, len(answer), spans)
|
|
48
|
+
return tuple(spans)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _push_answer_span(
|
|
52
|
+
answer: str,
|
|
53
|
+
start: int,
|
|
54
|
+
end: int,
|
|
55
|
+
spans: list[tuple[str, int, int]],
|
|
56
|
+
) -> None:
|
|
57
|
+
raw = answer[start:end]
|
|
58
|
+
text = raw.strip()
|
|
59
|
+
if not text:
|
|
60
|
+
return
|
|
61
|
+
leading = len(raw) - len(raw.lstrip())
|
|
62
|
+
trailing = len(raw) - len(raw.rstrip())
|
|
63
|
+
spans.append((text, start + leading, end - trailing))
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _q16_ratio(numerator: int, denominator: int) -> int:
|
|
67
|
+
if denominator == 0:
|
|
68
|
+
return 65535
|
|
69
|
+
return int(numerator * 65535 / denominator)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _unique(values: list[str] | list[int]) -> tuple[Any, ...]:
|
|
73
|
+
seen = set()
|
|
74
|
+
out = []
|
|
75
|
+
for value in values:
|
|
76
|
+
if value not in seen:
|
|
77
|
+
seen.add(value)
|
|
78
|
+
out.append(value)
|
|
79
|
+
return tuple(out)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def ground_answer(
|
|
83
|
+
context: ContextPackResponse,
|
|
84
|
+
answer: str,
|
|
85
|
+
*,
|
|
86
|
+
min_span_support_q16: int,
|
|
87
|
+
require_citations: bool,
|
|
88
|
+
reject_unsupported: bool,
|
|
89
|
+
) -> AnswerGroundingReportResponse:
|
|
90
|
+
spans: list[AnswerGroundingSpanResponse] = []
|
|
91
|
+
for text, start, end in _split_answer_spans(answer):
|
|
92
|
+
span_terms = _tokenize(text)
|
|
93
|
+
if not span_terms:
|
|
94
|
+
spans.append(
|
|
95
|
+
AnswerGroundingSpanResponse(
|
|
96
|
+
text=text,
|
|
97
|
+
start_byte=start,
|
|
98
|
+
end_byte=end,
|
|
99
|
+
support_q16=65535,
|
|
100
|
+
supported=True,
|
|
101
|
+
covered_terms=(),
|
|
102
|
+
missing_terms=(),
|
|
103
|
+
supported_by_cell_ids=(),
|
|
104
|
+
citations=(),
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
continue
|
|
108
|
+
covered: set[str] = set()
|
|
109
|
+
cell_ids: list[int] = []
|
|
110
|
+
citations: list[str] = []
|
|
111
|
+
for cell in context.cells:
|
|
112
|
+
cell_terms = set(_tokenize(cell.payload_text))
|
|
113
|
+
matched = False
|
|
114
|
+
for term in span_terms:
|
|
115
|
+
if term in cell_terms:
|
|
116
|
+
covered.add(term)
|
|
117
|
+
matched = True
|
|
118
|
+
if matched:
|
|
119
|
+
cell_ids.append(cell.cell_id)
|
|
120
|
+
if cell.citation:
|
|
121
|
+
citations.append(cell.citation)
|
|
122
|
+
support = _q16_ratio(len(covered), len(span_terms))
|
|
123
|
+
supported = support >= min_span_support_q16 and (
|
|
124
|
+
not require_citations or bool(citations)
|
|
125
|
+
)
|
|
126
|
+
spans.append(
|
|
127
|
+
AnswerGroundingSpanResponse(
|
|
128
|
+
text=text,
|
|
129
|
+
start_byte=start,
|
|
130
|
+
end_byte=end,
|
|
131
|
+
support_q16=support,
|
|
132
|
+
supported=supported,
|
|
133
|
+
covered_terms=tuple(sorted(covered)),
|
|
134
|
+
missing_terms=tuple(term for term in span_terms if term not in covered),
|
|
135
|
+
supported_by_cell_ids=_unique(cell_ids),
|
|
136
|
+
citations=_unique(citations),
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
supported_count = sum(1 for span in spans if span.supported)
|
|
140
|
+
unsupported_count = len(spans) - supported_count
|
|
141
|
+
average = int(sum(span.support_q16 for span in spans) / len(spans)) if spans else 65535
|
|
142
|
+
return AnswerGroundingReportResponse(
|
|
143
|
+
answer_supported=unsupported_count == 0,
|
|
144
|
+
rejected=reject_unsupported and unsupported_count > 0,
|
|
145
|
+
support_q16=average,
|
|
146
|
+
supported_span_count=supported_count,
|
|
147
|
+
unsupported_span_count=unsupported_count,
|
|
148
|
+
spans=tuple(spans),
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _grounded_answer_response(
|
|
153
|
+
*,
|
|
154
|
+
question: str,
|
|
155
|
+
answer: str,
|
|
156
|
+
retrieve_statement: str,
|
|
157
|
+
verify_statement: str | None,
|
|
158
|
+
context: ContextPackResponse,
|
|
159
|
+
verification: "VerificationReportResponse | None",
|
|
160
|
+
require_citations: bool,
|
|
161
|
+
reject_unsupported: bool,
|
|
162
|
+
) -> GroundedAnswerResponse:
|
|
163
|
+
grounding = context.ground_answer(
|
|
164
|
+
answer,
|
|
165
|
+
require_citations=require_citations,
|
|
166
|
+
reject_unsupported=reject_unsupported,
|
|
167
|
+
)
|
|
168
|
+
citations = _unique(
|
|
169
|
+
[
|
|
170
|
+
citation
|
|
171
|
+
for span in grounding.spans
|
|
172
|
+
for citation in span.citations
|
|
173
|
+
]
|
|
174
|
+
+ [cell.citation for cell in context.cells if cell.citation]
|
|
175
|
+
)
|
|
176
|
+
used_cell_ids = _unique(
|
|
177
|
+
[
|
|
178
|
+
cell_id
|
|
179
|
+
for span in grounding.spans
|
|
180
|
+
for cell_id in span.supported_by_cell_ids
|
|
181
|
+
]
|
|
182
|
+
+ [cell.cell_id for cell in context.cells]
|
|
183
|
+
)
|
|
184
|
+
return GroundedAnswerResponse(
|
|
185
|
+
question=question,
|
|
186
|
+
answer=answer,
|
|
187
|
+
retrieve_statement=retrieve_statement,
|
|
188
|
+
verify_statement=verify_statement,
|
|
189
|
+
context=context,
|
|
190
|
+
grounding=grounding,
|
|
191
|
+
verification=verification,
|
|
192
|
+
citations=citations,
|
|
193
|
+
used_context_cell_ids=used_cell_ids,
|
|
194
|
+
rejected=grounding.rejected,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from .context import (
|
|
2
|
+
AnswerGroundingReportResponse,
|
|
3
|
+
AnswerGroundingSpanResponse,
|
|
4
|
+
ContextPackAnomalyResponse,
|
|
5
|
+
ContextPackCellResponse,
|
|
6
|
+
ContextPackResponse,
|
|
7
|
+
ExplainResponse,
|
|
8
|
+
GroundedAnswerResponse,
|
|
9
|
+
SourceRefResponse,
|
|
10
|
+
)
|
|
11
|
+
from .core import (
|
|
12
|
+
AqlQueryCacheStatsResponse,
|
|
13
|
+
AqlCellResponse,
|
|
14
|
+
AqlResponse,
|
|
15
|
+
CellLookupResponse,
|
|
16
|
+
CellResponse,
|
|
17
|
+
HealthResponse,
|
|
18
|
+
PutCellResponse,
|
|
19
|
+
StatsResponse,
|
|
20
|
+
ValidationResponse,
|
|
21
|
+
)
|
|
22
|
+
from .ingestion import DeleteJobResponse, IngestResponse, IngestionJobResponse
|
|
23
|
+
from .memory import RememberResponse
|
|
24
|
+
from .search import (
|
|
25
|
+
AnnEvaluationResponse,
|
|
26
|
+
AnnNoFallbackDecision,
|
|
27
|
+
AnnSearchReport,
|
|
28
|
+
SearchResponse,
|
|
29
|
+
SearchResult,
|
|
30
|
+
SearchRoutingDecision,
|
|
31
|
+
)
|
|
32
|
+
from .verification import EvidenceResponse, GuardResponse, NumericConflictResponse, VerificationReportResponse
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
"AnnEvaluationResponse",
|
|
36
|
+
"AnnNoFallbackDecision",
|
|
37
|
+
"AnnSearchReport",
|
|
38
|
+
"AnswerGroundingReportResponse",
|
|
39
|
+
"AnswerGroundingSpanResponse",
|
|
40
|
+
"AqlCellResponse",
|
|
41
|
+
"AqlQueryCacheStatsResponse",
|
|
42
|
+
"AqlResponse",
|
|
43
|
+
"CellLookupResponse",
|
|
44
|
+
"CellResponse",
|
|
45
|
+
"ContextPackAnomalyResponse",
|
|
46
|
+
"ContextPackCellResponse",
|
|
47
|
+
"ContextPackResponse",
|
|
48
|
+
"DeleteJobResponse",
|
|
49
|
+
"EvidenceResponse",
|
|
50
|
+
"ExplainResponse",
|
|
51
|
+
"GroundedAnswerResponse",
|
|
52
|
+
"GuardResponse",
|
|
53
|
+
"HealthResponse",
|
|
54
|
+
"IngestResponse",
|
|
55
|
+
"IngestionJobResponse",
|
|
56
|
+
"NumericConflictResponse",
|
|
57
|
+
"PutCellResponse",
|
|
58
|
+
"RememberResponse",
|
|
59
|
+
"SearchResponse",
|
|
60
|
+
"SearchResult",
|
|
61
|
+
"SearchRoutingDecision",
|
|
62
|
+
"SourceRefResponse",
|
|
63
|
+
"StatsResponse",
|
|
64
|
+
"ValidationResponse",
|
|
65
|
+
"VerificationReportResponse",
|
|
66
|
+
]
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True)
|
|
8
|
+
class ExplainResponse:
|
|
9
|
+
score: int
|
|
10
|
+
matched_terms: tuple[str, ...]
|
|
11
|
+
why_selected: str
|
|
12
|
+
base_bm25: int
|
|
13
|
+
source_trust_bonus: int
|
|
14
|
+
redundancy_penalty: int
|
|
15
|
+
|
|
16
|
+
@classmethod
|
|
17
|
+
def from_json(cls, value: dict[str, Any]) -> "ExplainResponse":
|
|
18
|
+
return cls(
|
|
19
|
+
score=int(value["score"]),
|
|
20
|
+
matched_terms=tuple(str(row) for row in value["matched_terms"]),
|
|
21
|
+
why_selected=str(value["why_selected"]),
|
|
22
|
+
base_bm25=int(value["base_bm25"]),
|
|
23
|
+
source_trust_bonus=int(value["source_trust_bonus"]),
|
|
24
|
+
redundancy_penalty=int(value["redundancy_penalty"]),
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class SourceRefResponse:
|
|
30
|
+
source_id: str
|
|
31
|
+
document_id: str | None
|
|
32
|
+
page: int | None
|
|
33
|
+
cell_range: str | None
|
|
34
|
+
json_path: str | None
|
|
35
|
+
confidence_q16: int
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def from_json(cls, value: dict[str, Any]) -> "SourceRefResponse":
|
|
39
|
+
return cls(
|
|
40
|
+
source_id=str(value["source_id"]),
|
|
41
|
+
document_id=str(value["document_id"]) if value.get("document_id") is not None else None,
|
|
42
|
+
page=int(value["page"]) if value.get("page") is not None else None,
|
|
43
|
+
cell_range=str(value["cell_range"]) if value.get("cell_range") is not None else None,
|
|
44
|
+
json_path=str(value["json_path"]) if value.get("json_path") is not None else None,
|
|
45
|
+
confidence_q16=int(value["confidence_q16"]),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass(frozen=True)
|
|
50
|
+
class ContextPackCellResponse:
|
|
51
|
+
cell_id: int
|
|
52
|
+
estimated_tokens: int
|
|
53
|
+
citation: str | None
|
|
54
|
+
payload_text: str
|
|
55
|
+
explain: ExplainResponse | None
|
|
56
|
+
source_ref: SourceRefResponse | None
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def from_json(cls, value: dict[str, Any]) -> "ContextPackCellResponse":
|
|
60
|
+
explain = value.get("explain")
|
|
61
|
+
source_ref = value.get("source_ref")
|
|
62
|
+
return cls(
|
|
63
|
+
cell_id=int(value["cell_id"]),
|
|
64
|
+
estimated_tokens=int(value["estimated_tokens"]),
|
|
65
|
+
citation=str(value["citation"]) if value.get("citation") is not None else None,
|
|
66
|
+
payload_text=str(value["payload_text"]),
|
|
67
|
+
explain=ExplainResponse.from_json(explain) if explain else None,
|
|
68
|
+
source_ref=SourceRefResponse.from_json(source_ref) if source_ref else None,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass(frozen=True)
|
|
73
|
+
class ContextPackAnomalyResponse:
|
|
74
|
+
cell_id: int | None
|
|
75
|
+
code: str
|
|
76
|
+
message: str
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def from_json(cls, value: dict[str, Any]) -> "ContextPackAnomalyResponse":
|
|
80
|
+
return cls(
|
|
81
|
+
cell_id=int(value["cell_id"]) if value.get("cell_id") is not None else None,
|
|
82
|
+
code=str(value["code"]),
|
|
83
|
+
message=str(value["message"]),
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass(frozen=True)
|
|
88
|
+
class ContextPackResponse:
|
|
89
|
+
schema_version: str
|
|
90
|
+
token_budget_tokens: int
|
|
91
|
+
estimated_tokens: int
|
|
92
|
+
truncated: bool
|
|
93
|
+
citations_required: bool
|
|
94
|
+
cells: tuple[ContextPackCellResponse, ...]
|
|
95
|
+
anomalies: tuple[ContextPackAnomalyResponse, ...]
|
|
96
|
+
|
|
97
|
+
@classmethod
|
|
98
|
+
def from_json(cls, value: dict[str, Any]) -> "ContextPackResponse":
|
|
99
|
+
return cls(
|
|
100
|
+
schema_version=str(value["schema_version"]),
|
|
101
|
+
token_budget_tokens=int(value["token_budget_tokens"]),
|
|
102
|
+
estimated_tokens=int(value["estimated_tokens"]),
|
|
103
|
+
truncated=bool(value["truncated"]),
|
|
104
|
+
citations_required=bool(value["citations_required"]),
|
|
105
|
+
cells=tuple(ContextPackCellResponse.from_json(row) for row in value["cells"]),
|
|
106
|
+
anomalies=tuple(ContextPackAnomalyResponse.from_json(row) for row in value.get("anomalies", [])),
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
def ground_answer(
|
|
110
|
+
self,
|
|
111
|
+
answer: str,
|
|
112
|
+
*,
|
|
113
|
+
min_span_support_q16: int = 65535,
|
|
114
|
+
require_citations: bool = False,
|
|
115
|
+
reject_unsupported: bool = False,
|
|
116
|
+
) -> "AnswerGroundingReportResponse":
|
|
117
|
+
from ..grounding import ground_answer
|
|
118
|
+
|
|
119
|
+
return ground_answer(
|
|
120
|
+
self,
|
|
121
|
+
answer,
|
|
122
|
+
min_span_support_q16=min_span_support_q16,
|
|
123
|
+
require_citations=require_citations,
|
|
124
|
+
reject_unsupported=reject_unsupported,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@dataclass(frozen=True)
|
|
129
|
+
class AnswerGroundingSpanResponse:
|
|
130
|
+
text: str
|
|
131
|
+
start_byte: int
|
|
132
|
+
end_byte: int
|
|
133
|
+
support_q16: int
|
|
134
|
+
supported: bool
|
|
135
|
+
covered_terms: tuple[str, ...]
|
|
136
|
+
missing_terms: tuple[str, ...]
|
|
137
|
+
supported_by_cell_ids: tuple[int, ...]
|
|
138
|
+
citations: tuple[str, ...]
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass(frozen=True)
|
|
142
|
+
class AnswerGroundingReportResponse:
|
|
143
|
+
answer_supported: bool
|
|
144
|
+
rejected: bool
|
|
145
|
+
support_q16: int
|
|
146
|
+
supported_span_count: int
|
|
147
|
+
unsupported_span_count: int
|
|
148
|
+
spans: tuple[AnswerGroundingSpanResponse, ...]
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@dataclass(frozen=True)
|
|
152
|
+
class GroundedAnswerResponse:
|
|
153
|
+
question: str
|
|
154
|
+
answer: str
|
|
155
|
+
retrieve_statement: str
|
|
156
|
+
verify_statement: str | None
|
|
157
|
+
context: ContextPackResponse
|
|
158
|
+
grounding: AnswerGroundingReportResponse
|
|
159
|
+
verification: "VerificationReportResponse | None"
|
|
160
|
+
citations: tuple[str, ...]
|
|
161
|
+
used_context_cell_ids: tuple[int, ...]
|
|
162
|
+
rejected: bool
|
|
163
|
+
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True)
|
|
8
|
+
class HealthResponse:
|
|
9
|
+
status: str
|
|
10
|
+
version: str
|
|
11
|
+
server_version: str
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def from_json(cls, value: dict[str, Any]) -> "HealthResponse":
|
|
15
|
+
return cls(
|
|
16
|
+
status=str(value["status"]),
|
|
17
|
+
version=str(value["version"]),
|
|
18
|
+
server_version=str(value["server_version"]),
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True)
|
|
23
|
+
class AqlQueryCacheStatsResponse:
|
|
24
|
+
entries: int
|
|
25
|
+
max_entries: int
|
|
26
|
+
hits: int
|
|
27
|
+
misses: int
|
|
28
|
+
evictions: int
|
|
29
|
+
catalog_invalidations: int
|
|
30
|
+
hit_rate_q16: int
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def from_json(cls, value: dict[str, Any]) -> "AqlQueryCacheStatsResponse":
|
|
34
|
+
return cls(
|
|
35
|
+
entries=int(value.get("entries", 0)),
|
|
36
|
+
max_entries=int(value.get("max_entries", 0)),
|
|
37
|
+
hits=int(value.get("hits", 0)),
|
|
38
|
+
misses=int(value.get("misses", 0)),
|
|
39
|
+
evictions=int(value.get("evictions", 0)),
|
|
40
|
+
catalog_invalidations=int(value.get("catalog_invalidations", 0)),
|
|
41
|
+
hit_rate_q16=int(value.get("hit_rate_q16", 0)),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True)
|
|
46
|
+
class StatsResponse:
|
|
47
|
+
current_seq: int
|
|
48
|
+
checkpoint_seq: int
|
|
49
|
+
live_segments: int
|
|
50
|
+
retired_segments: int
|
|
51
|
+
memtable_cells: int
|
|
52
|
+
memtable_versions: int
|
|
53
|
+
memtable_payload_bytes: int
|
|
54
|
+
estimated_memtable_bytes: int
|
|
55
|
+
estimated_index_bytes: int
|
|
56
|
+
estimated_context_pack_bytes: int
|
|
57
|
+
estimated_total_memory_bytes: int
|
|
58
|
+
live_segment_bytes: int
|
|
59
|
+
retired_segment_bytes: int
|
|
60
|
+
total_segment_bytes: int
|
|
61
|
+
durable_storage_bytes: int
|
|
62
|
+
live_segment_payload_bytes: int
|
|
63
|
+
logical_payload_bytes: int
|
|
64
|
+
space_amplification_q16: int
|
|
65
|
+
write_amplification_q16: int
|
|
66
|
+
compaction_pressure_q16: int
|
|
67
|
+
wal_size_bytes: int
|
|
68
|
+
wal_writer_records: int
|
|
69
|
+
wal_writer_bytes: int
|
|
70
|
+
wal_writer_fsyncs: int
|
|
71
|
+
wal_writer_batches: int
|
|
72
|
+
aql_query_cache: AqlQueryCacheStatsResponse
|
|
73
|
+
|
|
74
|
+
@classmethod
|
|
75
|
+
def from_json(cls, value: dict[str, Any]) -> "StatsResponse":
|
|
76
|
+
return cls(
|
|
77
|
+
current_seq=int(value["current_seq"]),
|
|
78
|
+
checkpoint_seq=int(value["checkpoint_seq"]),
|
|
79
|
+
live_segments=int(value["live_segments"]),
|
|
80
|
+
retired_segments=int(value["retired_segments"]),
|
|
81
|
+
memtable_cells=int(value["memtable_cells"]),
|
|
82
|
+
memtable_versions=int(value["memtable_versions"]),
|
|
83
|
+
memtable_payload_bytes=int(value.get("memtable_payload_bytes", 0)),
|
|
84
|
+
estimated_memtable_bytes=int(value.get("estimated_memtable_bytes", 0)),
|
|
85
|
+
estimated_index_bytes=int(value.get("estimated_index_bytes", 0)),
|
|
86
|
+
estimated_context_pack_bytes=int(value.get("estimated_context_pack_bytes", 0)),
|
|
87
|
+
estimated_total_memory_bytes=int(value.get("estimated_total_memory_bytes", 0)),
|
|
88
|
+
live_segment_bytes=int(value.get("live_segment_bytes", 0)),
|
|
89
|
+
retired_segment_bytes=int(value.get("retired_segment_bytes", 0)),
|
|
90
|
+
total_segment_bytes=int(value.get("total_segment_bytes", 0)),
|
|
91
|
+
durable_storage_bytes=int(value.get("durable_storage_bytes", 0)),
|
|
92
|
+
live_segment_payload_bytes=int(value.get("live_segment_payload_bytes", 0)),
|
|
93
|
+
logical_payload_bytes=int(value.get("logical_payload_bytes", 0)),
|
|
94
|
+
space_amplification_q16=int(value.get("space_amplification_q16", 0)),
|
|
95
|
+
write_amplification_q16=int(value.get("write_amplification_q16", 0)),
|
|
96
|
+
compaction_pressure_q16=int(value.get("compaction_pressure_q16", 0)),
|
|
97
|
+
wal_size_bytes=int(value["wal_size_bytes"]),
|
|
98
|
+
wal_writer_records=int(value["wal_writer_records"]),
|
|
99
|
+
wal_writer_bytes=int(value["wal_writer_bytes"]),
|
|
100
|
+
wal_writer_fsyncs=int(value["wal_writer_fsyncs"]),
|
|
101
|
+
wal_writer_batches=int(value["wal_writer_batches"]),
|
|
102
|
+
aql_query_cache=AqlQueryCacheStatsResponse.from_json(
|
|
103
|
+
value.get("aql_query_cache", {})
|
|
104
|
+
),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclass(frozen=True)
|
|
109
|
+
class ValidationResponse:
|
|
110
|
+
ok: bool
|
|
111
|
+
manifest_ok: bool
|
|
112
|
+
wal_ok: bool
|
|
113
|
+
live_segments_checked: int
|
|
114
|
+
bitmap_indexes_checked: int
|
|
115
|
+
lexical_indexes_checked: int
|
|
116
|
+
vector_indexes_checked: int
|
|
117
|
+
hnsw_graphs_checked: int
|
|
118
|
+
cells_checked: int
|
|
119
|
+
wal_records_checked: int
|
|
120
|
+
wal_safe_truncate_offset: int
|
|
121
|
+
errors: tuple[str, ...]
|
|
122
|
+
|
|
123
|
+
@classmethod
|
|
124
|
+
def from_json(cls, value: dict[str, Any]) -> "ValidationResponse":
|
|
125
|
+
return cls(
|
|
126
|
+
ok=bool(value["ok"]),
|
|
127
|
+
manifest_ok=bool(value["manifest_ok"]),
|
|
128
|
+
wal_ok=bool(value["wal_ok"]),
|
|
129
|
+
live_segments_checked=int(value["live_segments_checked"]),
|
|
130
|
+
bitmap_indexes_checked=int(value["bitmap_indexes_checked"]),
|
|
131
|
+
lexical_indexes_checked=int(value["lexical_indexes_checked"]),
|
|
132
|
+
vector_indexes_checked=int(value["vector_indexes_checked"]),
|
|
133
|
+
hnsw_graphs_checked=int(value["hnsw_graphs_checked"]),
|
|
134
|
+
cells_checked=int(value["cells_checked"]),
|
|
135
|
+
wal_records_checked=int(value["wal_records_checked"]),
|
|
136
|
+
wal_safe_truncate_offset=int(value["wal_safe_truncate_offset"]),
|
|
137
|
+
errors=tuple(str(row) for row in value.get("errors", [])),
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass(frozen=True)
|
|
142
|
+
class PutCellResponse:
|
|
143
|
+
seq: int
|
|
144
|
+
cell_id: int
|
|
145
|
+
|
|
146
|
+
@classmethod
|
|
147
|
+
def from_json(cls, value: dict[str, Any]) -> "PutCellResponse":
|
|
148
|
+
return cls(seq=int(value["seq"]), cell_id=int(value["cell_id"]))
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@dataclass(frozen=True)
|
|
152
|
+
class CellResponse:
|
|
153
|
+
cell_id: int
|
|
154
|
+
payload: str
|
|
155
|
+
|
|
156
|
+
@classmethod
|
|
157
|
+
def from_json(cls, value: dict[str, Any]) -> "CellResponse":
|
|
158
|
+
return cls(cell_id=int(value["cell_id"]), payload=str(value["payload"]))
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@dataclass(frozen=True)
|
|
162
|
+
class CellLookupResponse:
|
|
163
|
+
cell: CellResponse | None
|
|
164
|
+
|
|
165
|
+
@classmethod
|
|
166
|
+
def from_json(cls, value: dict[str, Any]) -> "CellLookupResponse":
|
|
167
|
+
cell = value.get("cell")
|
|
168
|
+
return cls(cell=CellResponse.from_json(cell) if cell else None)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@dataclass(frozen=True)
|
|
172
|
+
class AqlCellResponse:
|
|
173
|
+
cell_id: int
|
|
174
|
+
payload: str
|
|
175
|
+
|
|
176
|
+
@classmethod
|
|
177
|
+
def from_json(cls, value: dict[str, Any]) -> "AqlCellResponse":
|
|
178
|
+
return cls(cell_id=int(value["cell_id"]), payload=str(value["payload"]))
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@dataclass(frozen=True)
|
|
182
|
+
class AqlResponse:
|
|
183
|
+
cells: tuple[AqlCellResponse, ...]
|
|
184
|
+
|
|
185
|
+
@classmethod
|
|
186
|
+
def from_json(cls, value: dict[str, Any]) -> "AqlResponse":
|
|
187
|
+
return cls(cells=tuple(AqlCellResponse.from_json(row) for row in value["cells"]))
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True)
|
|
8
|
+
class IngestResponse:
|
|
9
|
+
rows_ingested: int
|
|
10
|
+
chunks_ingested: int
|
|
11
|
+
facts_ingested: int
|
|
12
|
+
first_cell_id: int | None
|
|
13
|
+
job_id: int | None
|
|
14
|
+
validation_report: dict[str, Any]
|
|
15
|
+
|
|
16
|
+
@classmethod
|
|
17
|
+
def from_json(cls, value: dict[str, Any]) -> "IngestResponse":
|
|
18
|
+
return cls(
|
|
19
|
+
rows_ingested=int(value["rows_ingested"]),
|
|
20
|
+
chunks_ingested=int(value["chunks_ingested"]),
|
|
21
|
+
facts_ingested=int(value["facts_ingested"]),
|
|
22
|
+
first_cell_id=int(value["first_cell_id"]) if value.get("first_cell_id") is not None else None,
|
|
23
|
+
job_id=int(value["job_id"]) if value.get("job_id") is not None else None,
|
|
24
|
+
validation_report=dict(value.get("validation_report", {})),
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class IngestionJobResponse:
|
|
30
|
+
job_id: int
|
|
31
|
+
label: str
|
|
32
|
+
status: str
|
|
33
|
+
total_items: int | None
|
|
34
|
+
completed_items: int
|
|
35
|
+
failed_items: int
|
|
36
|
+
last_cell_id: int | None
|
|
37
|
+
message: str | None
|
|
38
|
+
retry_count: int = 0
|
|
39
|
+
max_retries: int = 3
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def from_json(cls, value: dict[str, Any]) -> "IngestionJobResponse":
|
|
43
|
+
return cls(
|
|
44
|
+
job_id=int(value["job_id"]),
|
|
45
|
+
label=str(value["label"]),
|
|
46
|
+
status=str(value["status"]),
|
|
47
|
+
total_items=int(value["total_items"]) if value.get("total_items") is not None else None,
|
|
48
|
+
completed_items=int(value["completed_items"]),
|
|
49
|
+
failed_items=int(value["failed_items"]),
|
|
50
|
+
last_cell_id=int(value["last_cell_id"]) if value.get("last_cell_id") is not None else None,
|
|
51
|
+
message=str(value["message"]) if value.get("message") is not None else None,
|
|
52
|
+
retry_count=int(value.get("retry_count", 0)),
|
|
53
|
+
max_retries=int(value.get("max_retries", 3)),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass(frozen=True)
|
|
58
|
+
class DeleteJobResponse:
|
|
59
|
+
deleted: bool
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def from_json(cls, value: dict[str, Any]) -> "DeleteJobResponse":
|
|
63
|
+
return cls(deleted=bool(value["deleted"]))
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True)
|
|
8
|
+
class RememberResponse:
|
|
9
|
+
seq: int
|
|
10
|
+
cell_id: int
|
|
11
|
+
ttl_seconds: int | None
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def from_json(cls, value: dict[str, Any]) -> "RememberResponse":
|
|
15
|
+
return cls(
|
|
16
|
+
seq=int(value["seq"]),
|
|
17
|
+
cell_id=int(value["cell_id"]),
|
|
18
|
+
ttl_seconds=int(value["ttl_seconds"]) if value.get("ttl_seconds") is not None else None,
|
|
19
|
+
)
|
|
20
|
+
|