cbrkit 0.28.2__tar.gz → 0.28.4__tar.gz
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.
- {cbrkit-0.28.2 → cbrkit-0.28.4}/PKG-INFO +2 -1
- {cbrkit-0.28.2 → cbrkit-0.28.4}/pyproject.toml +2 -1
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/eval/__init__.py +8 -1
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/eval/common.py +48 -3
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/eval/retrieval.py +10 -29
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/embed.py +1 -1
- cbrkit-0.28.4/src/cbrkit/system.py +250 -0
- cbrkit-0.28.2/src/cbrkit/system.py +0 -152
- {cbrkit-0.28.2 → cbrkit-0.28.4}/README.md +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/__init__.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/__main__.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/adapt/__init__.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/adapt/attribute_value.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/adapt/generic.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/adapt/numbers.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/adapt/strings.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/api.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/cli.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/constants.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/cycle.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/dumpers.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/helpers.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/loaders.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/model/__init__.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/model/graph.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/model/result.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/py.typed +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/retrieval/__init__.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/retrieval/apply.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/retrieval/build.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/retrieval/rerank.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/reuse/__init__.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/reuse/apply.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/reuse/build.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/__init__.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/aggregator.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/attribute_value.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/collections.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/generic.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/__init__.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/alignment.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/astar.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/brute_force.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/common.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/dfs.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/greedy.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/lap.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/precompute.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/qap.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/vf2.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/numbers.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/pooling.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/strings.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/taxonomy.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/wrappers.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/__init__.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/apply.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/build.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/model.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/prompts.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/__init__.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/anthropic.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/cohere.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/google.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/instructor.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/model.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/ollama.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/openai.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/openai_agents.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/pydantic_ai.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/wrappers.py +0 -0
- {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/typing.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: cbrkit
|
|
3
|
-
Version: 0.28.
|
|
3
|
+
Version: 0.28.4
|
|
4
4
|
Summary: Customizable Case-Based Reasoning (CBR) toolkit for Python with a built-in API and CLI
|
|
5
5
|
Keywords: cbr,case-based reasoning,api,similarity,nlp,retrieval,cli,tool,library
|
|
6
6
|
Author: Mirko Lenz
|
|
@@ -40,6 +40,7 @@ Requires-Dist: pydantic-settings>=2,<3 ; extra == 'api'
|
|
|
40
40
|
Requires-Dist: python-multipart>=0.0.15,<1 ; extra == 'api'
|
|
41
41
|
Requires-Dist: uvicorn[standard]>=0.30,<1 ; extra == 'api'
|
|
42
42
|
Requires-Dist: fastmcp>=2,<3 ; extra == 'api'
|
|
43
|
+
Requires-Dist: jsonref>=1,<2 ; extra == 'api'
|
|
43
44
|
Requires-Dist: chonkie>=1,<2 ; extra == 'chunking'
|
|
44
45
|
Requires-Dist: typer>=0.9,<1 ; extra == 'cli'
|
|
45
46
|
Requires-Dist: ranx>=0.3,<1 ; extra == 'eval'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "cbrkit"
|
|
3
|
-
version = "0.28.
|
|
3
|
+
version = "0.28.4"
|
|
4
4
|
description = "Customizable Case-Based Reasoning (CBR) toolkit for Python with a built-in API and CLI"
|
|
5
5
|
authors = [{ name = "Mirko Lenz", email = "mirko@mirkolenz.com" }]
|
|
6
6
|
readme = "README.md"
|
|
@@ -58,6 +58,7 @@ api = [
|
|
|
58
58
|
"python-multipart>=0.0.15,<1",
|
|
59
59
|
"uvicorn[standard]>=0.30,<1",
|
|
60
60
|
"fastmcp>=2,<3",
|
|
61
|
+
"jsonref>=1,<2"
|
|
61
62
|
]
|
|
62
63
|
chunking = ["chonkie>=1,<2"]
|
|
63
64
|
cli = ["typer>=0.9,<1"]
|
|
@@ -4,7 +4,13 @@ Please refer to the official documentation for more information on the available
|
|
|
4
4
|
<https://amenra.github.io/ranx/metrics/>
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from .common import
|
|
7
|
+
from .common import (
|
|
8
|
+
compute,
|
|
9
|
+
compute_score_metrics,
|
|
10
|
+
generate_metrics,
|
|
11
|
+
parse_metric,
|
|
12
|
+
similarities_to_qrels,
|
|
13
|
+
)
|
|
8
14
|
from .retrieval import (
|
|
9
15
|
retrieval,
|
|
10
16
|
retrieval_step,
|
|
@@ -17,6 +23,7 @@ __all__ = [
|
|
|
17
23
|
"generate_metrics",
|
|
18
24
|
"parse_metric",
|
|
19
25
|
"compute_score_metrics",
|
|
26
|
+
"similarities_to_qrels",
|
|
20
27
|
"retrieval",
|
|
21
28
|
"retrieval_step",
|
|
22
29
|
"retrieval_step_to_qrels",
|
|
@@ -2,9 +2,16 @@ import itertools
|
|
|
2
2
|
import statistics
|
|
3
3
|
import warnings
|
|
4
4
|
from collections.abc import Callable, Iterable, Mapping, Sequence
|
|
5
|
-
from typing import Any, cast
|
|
6
|
-
|
|
7
|
-
from ..helpers import
|
|
5
|
+
from typing import Any, Literal, cast
|
|
6
|
+
|
|
7
|
+
from ..helpers import (
|
|
8
|
+
get_logger,
|
|
9
|
+
normalize_and_scale,
|
|
10
|
+
round,
|
|
11
|
+
sim_map2ranking,
|
|
12
|
+
unpack_float,
|
|
13
|
+
unpack_floats,
|
|
14
|
+
)
|
|
8
15
|
from ..typing import ConversionFunc, EvalMetricFunc, Float, QueryCaseMatrix
|
|
9
16
|
|
|
10
17
|
logger = get_logger(__name__)
|
|
@@ -388,3 +395,41 @@ def generate_metrics(
|
|
|
388
395
|
generate_metric(*args)
|
|
389
396
|
for args in itertools.product(metrics, ks, relevance_levels)
|
|
390
397
|
]
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def similarities_to_qrels[Q, C](
|
|
401
|
+
similarities: QueryCaseMatrix[Q, C, float],
|
|
402
|
+
max_qrel: int | None = None,
|
|
403
|
+
min_qrel: int = 1,
|
|
404
|
+
round_mode: Literal["floor", "ceil", "nearest"] = "nearest",
|
|
405
|
+
auto_scale: bool = True,
|
|
406
|
+
) -> QueryCaseMatrix[Q, C, int]:
|
|
407
|
+
if max_qrel is None:
|
|
408
|
+
return {
|
|
409
|
+
query: {
|
|
410
|
+
case: rank
|
|
411
|
+
for rank, case in enumerate(
|
|
412
|
+
reversed(sim_map2ranking(case_sims)),
|
|
413
|
+
start=min_qrel,
|
|
414
|
+
)
|
|
415
|
+
}
|
|
416
|
+
for query, case_sims in similarities.items()
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if auto_scale:
|
|
420
|
+
min_sim = min(min(entries.values()) for entries in similarities.values())
|
|
421
|
+
max_sim = max(max(entries.values()) for entries in similarities.values())
|
|
422
|
+
else:
|
|
423
|
+
min_sim = 0.0
|
|
424
|
+
max_sim = 1.0
|
|
425
|
+
|
|
426
|
+
return {
|
|
427
|
+
query: {
|
|
428
|
+
case: round(
|
|
429
|
+
normalize_and_scale(sim, min_sim, max_sim, min_qrel, max_qrel),
|
|
430
|
+
round_mode,
|
|
431
|
+
)
|
|
432
|
+
for case, sim in case_sims.items()
|
|
433
|
+
}
|
|
434
|
+
for query, case_sims in similarities.items()
|
|
435
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
from collections.abc import Sequence
|
|
2
2
|
from typing import Any, Literal
|
|
3
3
|
|
|
4
|
-
from ..helpers import
|
|
4
|
+
from ..helpers import unpack_float
|
|
5
5
|
from ..retrieval import Result, ResultStep
|
|
6
6
|
from ..typing import EvalMetricFunc, Float, QueryCaseMatrix
|
|
7
|
-
from .common import DEFAULT_METRICS, compute
|
|
7
|
+
from .common import DEFAULT_METRICS, compute, similarities_to_qrels
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def retrieval_step[Q, C, S: Float](
|
|
@@ -45,36 +45,17 @@ def retrieval_step_to_qrels[Q, C, S: Float](
|
|
|
45
45
|
round_mode: Literal["floor", "ceil", "nearest"] = "nearest",
|
|
46
46
|
auto_scale: bool = True,
|
|
47
47
|
) -> QueryCaseMatrix[Q, C, int]:
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
query: {
|
|
51
|
-
case: rank
|
|
52
|
-
for rank, case in enumerate(reversed(entry.ranking), start=min_qrel)
|
|
53
|
-
}
|
|
54
|
-
for query, entry in result.queries.items()
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
sims = {
|
|
48
|
+
unpacked_sims = {
|
|
58
49
|
query: {case: unpack_float(value) for case, value in entry.similarities.items()}
|
|
59
50
|
for query, entry in result.queries.items()
|
|
60
51
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
query: {
|
|
70
|
-
case: round(
|
|
71
|
-
normalize_and_scale(sim, min_sim, max_sim, min_qrel, max_qrel),
|
|
72
|
-
round_mode,
|
|
73
|
-
)
|
|
74
|
-
for case, sim in entry.items()
|
|
75
|
-
}
|
|
76
|
-
for query, entry in sims.items()
|
|
77
|
-
}
|
|
52
|
+
return similarities_to_qrels(
|
|
53
|
+
unpacked_sims,
|
|
54
|
+
max_qrel,
|
|
55
|
+
min_qrel,
|
|
56
|
+
round_mode,
|
|
57
|
+
auto_scale,
|
|
58
|
+
)
|
|
78
59
|
|
|
79
60
|
|
|
80
61
|
def retrieval_to_qrels[Q, C, S: Float](
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Annotated, Any, cast
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
import cbrkit
|
|
8
|
+
from cbrkit.typing import Float, MaybeSequence
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"System",
|
|
12
|
+
"to_fastapi",
|
|
13
|
+
"to_fastmcp",
|
|
14
|
+
"to_pydantic_ai",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(slots=True, frozen=True)
|
|
19
|
+
class System[
|
|
20
|
+
K: str | int,
|
|
21
|
+
V: BaseModel,
|
|
22
|
+
S: Float,
|
|
23
|
+
R1: BaseModel | None,
|
|
24
|
+
R2: BaseModel | None,
|
|
25
|
+
]:
|
|
26
|
+
casebase: cbrkit.typing.Casebase[K, V]
|
|
27
|
+
retriever_factory: (
|
|
28
|
+
Callable[[R1], MaybeSequence[cbrkit.typing.RetrieverFunc[K, V, S]]] | None
|
|
29
|
+
) = None
|
|
30
|
+
reuser_factory: (
|
|
31
|
+
Callable[[R2], MaybeSequence[cbrkit.typing.ReuserFunc[K, V, S]]] | None
|
|
32
|
+
) = None
|
|
33
|
+
|
|
34
|
+
def retrieve(
|
|
35
|
+
self,
|
|
36
|
+
query: V,
|
|
37
|
+
config: R1,
|
|
38
|
+
) -> cbrkit.retrieval.QueryResultStep[K, V, S]:
|
|
39
|
+
if not self.retriever_factory:
|
|
40
|
+
raise ValueError("Retriever factory is not defined.")
|
|
41
|
+
|
|
42
|
+
return cbrkit.retrieval.apply_query(
|
|
43
|
+
self.casebase,
|
|
44
|
+
query,
|
|
45
|
+
self.retriever_factory(config),
|
|
46
|
+
).default_query
|
|
47
|
+
|
|
48
|
+
def reuse(
|
|
49
|
+
self,
|
|
50
|
+
query: V,
|
|
51
|
+
config: R2,
|
|
52
|
+
) -> cbrkit.retrieval.QueryResultStep[K, V, S]:
|
|
53
|
+
if not self.reuser_factory:
|
|
54
|
+
raise ValueError("Reuser factory is not defined.")
|
|
55
|
+
|
|
56
|
+
return cbrkit.reuse.apply_query(
|
|
57
|
+
self.casebase,
|
|
58
|
+
query,
|
|
59
|
+
self.reuser_factory(config),
|
|
60
|
+
).default_query
|
|
61
|
+
|
|
62
|
+
def cycle(
|
|
63
|
+
self,
|
|
64
|
+
query: V,
|
|
65
|
+
retrieve_config: R1,
|
|
66
|
+
reuse_config: R2,
|
|
67
|
+
) -> cbrkit.retrieval.QueryResultStep[K, V, S]:
|
|
68
|
+
if not self.retriever_factory or not self.reuser_factory:
|
|
69
|
+
raise ValueError("Retriever or reuser factory is not defined.")
|
|
70
|
+
|
|
71
|
+
return cbrkit.cycle.apply_query(
|
|
72
|
+
self.casebase,
|
|
73
|
+
query,
|
|
74
|
+
self.retriever_factory(retrieve_config),
|
|
75
|
+
self.reuser_factory(reuse_config),
|
|
76
|
+
).final_step.default_query
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
with cbrkit.helpers.optional_dependencies():
|
|
80
|
+
from fastapi import APIRouter, Body, FastAPI
|
|
81
|
+
|
|
82
|
+
@dataclass(slots=True, frozen=True)
|
|
83
|
+
class FastAPISystem[
|
|
84
|
+
K: str | int,
|
|
85
|
+
V: BaseModel,
|
|
86
|
+
S: Float,
|
|
87
|
+
R1: BaseModel | None,
|
|
88
|
+
R2: BaseModel | None,
|
|
89
|
+
]:
|
|
90
|
+
system: System[K, V, S, R1, R2]
|
|
91
|
+
|
|
92
|
+
def retrieve(
|
|
93
|
+
self,
|
|
94
|
+
query: Annotated[V, Body],
|
|
95
|
+
config: R1,
|
|
96
|
+
) -> cbrkit.retrieval.QueryResultStep[K, V, S]:
|
|
97
|
+
return self.system.retrieve(query, config)
|
|
98
|
+
|
|
99
|
+
def reuse(
|
|
100
|
+
self,
|
|
101
|
+
query: Annotated[V, Body],
|
|
102
|
+
config: R2,
|
|
103
|
+
) -> cbrkit.retrieval.QueryResultStep[K, V, S]:
|
|
104
|
+
return self.system.reuse(query, config)
|
|
105
|
+
|
|
106
|
+
def cycle(
|
|
107
|
+
self,
|
|
108
|
+
query: Annotated[V, Body],
|
|
109
|
+
retrieve_config: R1,
|
|
110
|
+
reuse_config: R2,
|
|
111
|
+
) -> cbrkit.retrieval.QueryResultStep[K, V, S]:
|
|
112
|
+
return self.system.cycle(query, retrieve_config, reuse_config)
|
|
113
|
+
|
|
114
|
+
def casebase(self) -> cbrkit.typing.Casebase[K, V]:
|
|
115
|
+
return self.system.casebase
|
|
116
|
+
|
|
117
|
+
def case(self, key: K) -> V | None:
|
|
118
|
+
return self.system.casebase.get(key)
|
|
119
|
+
|
|
120
|
+
def to_fastapi[
|
|
121
|
+
K: str | int,
|
|
122
|
+
V: BaseModel,
|
|
123
|
+
S: Float,
|
|
124
|
+
R1: BaseModel | None,
|
|
125
|
+
R2: BaseModel | None,
|
|
126
|
+
T: APIRouter | FastAPI,
|
|
127
|
+
](system: System[K, V, S, R1, R2], app: T) -> T:
|
|
128
|
+
fastapi_system = FastAPISystem(system)
|
|
129
|
+
|
|
130
|
+
if system.retriever_factory:
|
|
131
|
+
app.post("/retrieve")(fastapi_system.retrieve)
|
|
132
|
+
|
|
133
|
+
if system.reuser_factory:
|
|
134
|
+
app.post("/reuse")(fastapi_system.reuse)
|
|
135
|
+
|
|
136
|
+
if system.retriever_factory and system.reuser_factory:
|
|
137
|
+
app.post("/cycle")(fastapi_system.cycle)
|
|
138
|
+
|
|
139
|
+
app.get("/casebase")(fastapi_system.casebase)
|
|
140
|
+
|
|
141
|
+
app.get("/casebase/{key}")(fastapi_system.case)
|
|
142
|
+
|
|
143
|
+
return app
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
with cbrkit.helpers.optional_dependencies():
|
|
147
|
+
import jsonref
|
|
148
|
+
from fastmcp import FastMCP
|
|
149
|
+
from fastmcp.tools.tool import FunctionTool
|
|
150
|
+
from fastmcp.utilities.json_schema import compress_schema
|
|
151
|
+
|
|
152
|
+
# https://github.com/jlowin/fastmcp/pull/1427
|
|
153
|
+
def dereference_schema(schema: dict[str, Any]) -> dict[str, Any]:
|
|
154
|
+
return compress_schema(
|
|
155
|
+
cast(
|
|
156
|
+
dict[str, Any],
|
|
157
|
+
jsonref.replace_refs(
|
|
158
|
+
schema,
|
|
159
|
+
jsonschema=True,
|
|
160
|
+
merge_props=True,
|
|
161
|
+
lazy_load=False,
|
|
162
|
+
proxies=False,
|
|
163
|
+
),
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
def dereference_tool(tool: FunctionTool) -> None:
|
|
168
|
+
tool.parameters = dereference_schema(tool.parameters)
|
|
169
|
+
|
|
170
|
+
if tool.output_schema is not None:
|
|
171
|
+
tool.output_schema = dereference_schema(tool.output_schema)
|
|
172
|
+
|
|
173
|
+
@dataclass(slots=True, frozen=True)
|
|
174
|
+
class FastMCPSystem[
|
|
175
|
+
K: str | int,
|
|
176
|
+
V: BaseModel,
|
|
177
|
+
S: Float,
|
|
178
|
+
R1: BaseModel | None,
|
|
179
|
+
R2: BaseModel | None,
|
|
180
|
+
]:
|
|
181
|
+
system: System[K, V, S, R1, R2]
|
|
182
|
+
|
|
183
|
+
def retrieve(
|
|
184
|
+
self,
|
|
185
|
+
query: V,
|
|
186
|
+
config: R1,
|
|
187
|
+
) -> cbrkit.retrieval.QueryResultStep[K, V, S]:
|
|
188
|
+
return self.system.retrieve(query, config)
|
|
189
|
+
|
|
190
|
+
def reuse(
|
|
191
|
+
self,
|
|
192
|
+
query: V,
|
|
193
|
+
config: R2,
|
|
194
|
+
) -> cbrkit.retrieval.QueryResultStep[K, V, S]:
|
|
195
|
+
return self.system.reuse(query, config)
|
|
196
|
+
|
|
197
|
+
def cycle(
|
|
198
|
+
self,
|
|
199
|
+
query: V,
|
|
200
|
+
retrieve_config: R1,
|
|
201
|
+
reuse_config: R2,
|
|
202
|
+
) -> cbrkit.retrieval.QueryResultStep[K, V, S]:
|
|
203
|
+
return self.system.cycle(query, retrieve_config, reuse_config)
|
|
204
|
+
|
|
205
|
+
def casebase(self) -> cbrkit.typing.Casebase[K, V]:
|
|
206
|
+
return self.system.casebase
|
|
207
|
+
|
|
208
|
+
def case(self, key: K) -> V | None:
|
|
209
|
+
return self.system.casebase.get(key)
|
|
210
|
+
|
|
211
|
+
def to_fastmcp[
|
|
212
|
+
K: str | int,
|
|
213
|
+
V: BaseModel,
|
|
214
|
+
S: Float,
|
|
215
|
+
R1: BaseModel | None,
|
|
216
|
+
R2: BaseModel | None,
|
|
217
|
+
T: FastMCP,
|
|
218
|
+
](system: System[K, V, S, R1, R2], app: T) -> T:
|
|
219
|
+
fastmcp_system = FastMCPSystem(system)
|
|
220
|
+
|
|
221
|
+
if system.retriever_factory:
|
|
222
|
+
dereference_tool(app.tool("retrieve")(fastmcp_system.retrieve))
|
|
223
|
+
|
|
224
|
+
if system.reuser_factory:
|
|
225
|
+
dereference_tool(app.tool("reuse")(fastmcp_system.reuse))
|
|
226
|
+
|
|
227
|
+
if system.retriever_factory and system.reuser_factory:
|
|
228
|
+
dereference_tool(app.tool("cycle")(fastmcp_system.cycle))
|
|
229
|
+
|
|
230
|
+
app.resource("casebase://{key}")(fastmcp_system.case)
|
|
231
|
+
|
|
232
|
+
return app
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
with cbrkit.helpers.optional_dependencies():
|
|
236
|
+
from pydantic_ai.toolsets import FunctionToolset
|
|
237
|
+
|
|
238
|
+
def to_pydantic_ai(system: System) -> FunctionToolset[None]:
|
|
239
|
+
tools: list[Callable[..., Any]] = []
|
|
240
|
+
|
|
241
|
+
if system.retriever_factory:
|
|
242
|
+
tools.append(system.retrieve)
|
|
243
|
+
|
|
244
|
+
if system.reuser_factory:
|
|
245
|
+
tools.append(system.reuse)
|
|
246
|
+
|
|
247
|
+
if system.retriever_factory and system.reuser_factory:
|
|
248
|
+
tools.append(system.cycle)
|
|
249
|
+
|
|
250
|
+
return FunctionToolset(tools)
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
from collections.abc import Callable, Mapping, Sequence
|
|
2
|
-
from dataclasses import dataclass, field
|
|
3
|
-
|
|
4
|
-
from pydantic import BaseModel
|
|
5
|
-
from typing_extensions import Any
|
|
6
|
-
|
|
7
|
-
import cbrkit
|
|
8
|
-
from cbrkit.helpers import produce_sequence
|
|
9
|
-
from cbrkit.typing import Float, MaybeSequence
|
|
10
|
-
|
|
11
|
-
__all__ = [
|
|
12
|
-
"System",
|
|
13
|
-
"to_fastapi",
|
|
14
|
-
"to_fastmcp",
|
|
15
|
-
"to_pydantic_ai",
|
|
16
|
-
]
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@dataclass(slots=True, frozen=True)
|
|
20
|
-
class System[K: str | int, V: BaseModel, S: Float, P: str]:
|
|
21
|
-
casebase: cbrkit.typing.Casebase[K, V]
|
|
22
|
-
retriever_pipelines: Mapping[
|
|
23
|
-
P, MaybeSequence[cbrkit.typing.RetrieverFunc[K, V, S]]
|
|
24
|
-
] = field(default_factory=dict)
|
|
25
|
-
reuser_pipelines: Mapping[P, MaybeSequence[cbrkit.typing.ReuserFunc[K, V, S]]] = (
|
|
26
|
-
field(default_factory=dict)
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
def get_retriever_pipeline(
|
|
30
|
-
self, name: P, limit: int | None
|
|
31
|
-
) -> Sequence[cbrkit.typing.RetrieverFunc[K, V, S]]:
|
|
32
|
-
retrievers = produce_sequence(self.retriever_pipelines[name])
|
|
33
|
-
|
|
34
|
-
if limit is not None:
|
|
35
|
-
*head_retrievers, tail_retriever = retrievers
|
|
36
|
-
retrievers = head_retrievers + [
|
|
37
|
-
cbrkit.retrieval.dropout(tail_retriever, limit=limit)
|
|
38
|
-
]
|
|
39
|
-
|
|
40
|
-
return retrievers
|
|
41
|
-
|
|
42
|
-
def retrieve(
|
|
43
|
-
self,
|
|
44
|
-
query: V,
|
|
45
|
-
retriever_pipeline: P,
|
|
46
|
-
limit: int | None = None,
|
|
47
|
-
) -> cbrkit.retrieval.QueryResultStep[K, V, S]:
|
|
48
|
-
return cbrkit.retrieval.apply_query(
|
|
49
|
-
self.casebase,
|
|
50
|
-
query,
|
|
51
|
-
self.get_retriever_pipeline(retriever_pipeline, limit),
|
|
52
|
-
).default_query
|
|
53
|
-
|
|
54
|
-
def reuse(
|
|
55
|
-
self,
|
|
56
|
-
query: V,
|
|
57
|
-
reuser_pipeline: P,
|
|
58
|
-
) -> cbrkit.retrieval.QueryResultStep[K, V, S]:
|
|
59
|
-
return cbrkit.reuse.apply_query(
|
|
60
|
-
self.casebase,
|
|
61
|
-
query,
|
|
62
|
-
self.reuser_pipelines[reuser_pipeline],
|
|
63
|
-
).default_query
|
|
64
|
-
|
|
65
|
-
def cycle(
|
|
66
|
-
self,
|
|
67
|
-
query: V,
|
|
68
|
-
retriever_pipeline: P,
|
|
69
|
-
reuser_pipeline: P,
|
|
70
|
-
limit: int | None = None,
|
|
71
|
-
) -> cbrkit.retrieval.QueryResultStep[K, V, S]:
|
|
72
|
-
return cbrkit.cycle.apply_query(
|
|
73
|
-
self.casebase,
|
|
74
|
-
query,
|
|
75
|
-
self.get_retriever_pipeline(retriever_pipeline, limit),
|
|
76
|
-
self.reuser_pipelines[reuser_pipeline],
|
|
77
|
-
).final_step.default_query
|
|
78
|
-
|
|
79
|
-
@property
|
|
80
|
-
def tools(self) -> list[Callable[..., Any]]:
|
|
81
|
-
res: list[Callable[..., Any]] = []
|
|
82
|
-
|
|
83
|
-
if self.retriever_pipelines:
|
|
84
|
-
res.append(self.retrieve)
|
|
85
|
-
|
|
86
|
-
if self.reuser_pipelines:
|
|
87
|
-
res.append(self.reuse)
|
|
88
|
-
|
|
89
|
-
if self.retriever_pipelines and self.reuser_pipelines:
|
|
90
|
-
res.append(self.cycle)
|
|
91
|
-
|
|
92
|
-
return res
|
|
93
|
-
|
|
94
|
-
def get_case(self, name: K) -> V:
|
|
95
|
-
return self.casebase[name]
|
|
96
|
-
|
|
97
|
-
def get_retriever_names(self) -> list[str]:
|
|
98
|
-
return list(self.retriever_pipelines.keys())
|
|
99
|
-
|
|
100
|
-
def get_reuser_names(self) -> list[str]:
|
|
101
|
-
return list(self.reuser_pipelines.keys())
|
|
102
|
-
|
|
103
|
-
@property
|
|
104
|
-
def resources(self) -> dict[str, Callable[..., Any]]:
|
|
105
|
-
return {
|
|
106
|
-
"casebase/{name}": self.get_case,
|
|
107
|
-
"pipelines/retrieve": self.get_retriever_names,
|
|
108
|
-
"pipelines/reuse": self.get_reuser_names,
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
@property
|
|
112
|
-
def prompts(self) -> list[Callable[..., Any]]:
|
|
113
|
-
return []
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
with cbrkit.helpers.optional_dependencies():
|
|
117
|
-
from fastapi import FastAPI
|
|
118
|
-
|
|
119
|
-
def to_fastapi(system: System, app: FastAPI) -> FastAPI:
|
|
120
|
-
for value in system.tools:
|
|
121
|
-
app.post(f"/tool/{value.__name__}")(value)
|
|
122
|
-
|
|
123
|
-
for key, value in system.resources.items():
|
|
124
|
-
app.get(f"/resource/{key}")(value)
|
|
125
|
-
|
|
126
|
-
for value in system.prompts:
|
|
127
|
-
app.post(f"/prompt/{value.__name__}")(value)
|
|
128
|
-
|
|
129
|
-
return app
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
with cbrkit.helpers.optional_dependencies():
|
|
133
|
-
from fastmcp import FastMCP
|
|
134
|
-
|
|
135
|
-
def to_fastmcp[T](system: System, app: FastMCP[T]) -> FastMCP[T]:
|
|
136
|
-
for value in system.tools:
|
|
137
|
-
app.tool(value)
|
|
138
|
-
|
|
139
|
-
for key, value in system.resources.items():
|
|
140
|
-
app.resource(f"cbrkit://{key}")(value)
|
|
141
|
-
|
|
142
|
-
for value in system.prompts:
|
|
143
|
-
app.prompt(value)
|
|
144
|
-
|
|
145
|
-
return app
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
with cbrkit.helpers.optional_dependencies():
|
|
149
|
-
from pydantic_ai.toolsets import FunctionToolset
|
|
150
|
-
|
|
151
|
-
def to_pydantic_ai(system: System) -> FunctionToolset[None]:
|
|
152
|
-
return FunctionToolset(system.tools)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|