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.
Files changed (72) hide show
  1. {cbrkit-0.28.2 → cbrkit-0.28.4}/PKG-INFO +2 -1
  2. {cbrkit-0.28.2 → cbrkit-0.28.4}/pyproject.toml +2 -1
  3. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/eval/__init__.py +8 -1
  4. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/eval/common.py +48 -3
  5. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/eval/retrieval.py +10 -29
  6. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/embed.py +1 -1
  7. cbrkit-0.28.4/src/cbrkit/system.py +250 -0
  8. cbrkit-0.28.2/src/cbrkit/system.py +0 -152
  9. {cbrkit-0.28.2 → cbrkit-0.28.4}/README.md +0 -0
  10. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/__init__.py +0 -0
  11. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/__main__.py +0 -0
  12. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/adapt/__init__.py +0 -0
  13. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/adapt/attribute_value.py +0 -0
  14. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/adapt/generic.py +0 -0
  15. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/adapt/numbers.py +0 -0
  16. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/adapt/strings.py +0 -0
  17. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/api.py +0 -0
  18. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/cli.py +0 -0
  19. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/constants.py +0 -0
  20. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/cycle.py +0 -0
  21. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/dumpers.py +0 -0
  22. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/helpers.py +0 -0
  23. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/loaders.py +0 -0
  24. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/model/__init__.py +0 -0
  25. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/model/graph.py +0 -0
  26. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/model/result.py +0 -0
  27. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/py.typed +0 -0
  28. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/retrieval/__init__.py +0 -0
  29. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/retrieval/apply.py +0 -0
  30. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/retrieval/build.py +0 -0
  31. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/retrieval/rerank.py +0 -0
  32. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/reuse/__init__.py +0 -0
  33. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/reuse/apply.py +0 -0
  34. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/reuse/build.py +0 -0
  35. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/__init__.py +0 -0
  36. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/aggregator.py +0 -0
  37. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/attribute_value.py +0 -0
  38. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/collections.py +0 -0
  39. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/generic.py +0 -0
  40. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/__init__.py +0 -0
  41. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/alignment.py +0 -0
  42. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/astar.py +0 -0
  43. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/brute_force.py +0 -0
  44. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/common.py +0 -0
  45. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/dfs.py +0 -0
  46. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/greedy.py +0 -0
  47. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/lap.py +0 -0
  48. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/precompute.py +0 -0
  49. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/qap.py +0 -0
  50. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/graphs/vf2.py +0 -0
  51. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/numbers.py +0 -0
  52. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/pooling.py +0 -0
  53. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/strings.py +0 -0
  54. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/taxonomy.py +0 -0
  55. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/sim/wrappers.py +0 -0
  56. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/__init__.py +0 -0
  57. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/apply.py +0 -0
  58. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/build.py +0 -0
  59. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/model.py +0 -0
  60. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/prompts.py +0 -0
  61. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/__init__.py +0 -0
  62. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/anthropic.py +0 -0
  63. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/cohere.py +0 -0
  64. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/google.py +0 -0
  65. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/instructor.py +0 -0
  66. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/model.py +0 -0
  67. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/ollama.py +0 -0
  68. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/openai.py +0 -0
  69. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/openai_agents.py +0 -0
  70. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/pydantic_ai.py +0 -0
  71. {cbrkit-0.28.2 → cbrkit-0.28.4}/src/cbrkit/synthesis/providers/wrappers.py +0 -0
  72. {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.2
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.2"
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 compute, compute_score_metrics, generate_metrics, parse_metric
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 get_logger, unpack_float, unpack_floats
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 normalize_and_scale, round, unpack_float
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
- if max_qrel is None:
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
- if auto_scale:
62
- min_sim = min(min(entries.values()) for entries in sims.values())
63
- max_sim = max(max(entries.values()) for entries in sims.values())
64
- else:
65
- min_sim = 0.0
66
- max_sim = 1.0
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](
@@ -197,7 +197,7 @@ class cache(BatchConversionFunc[str, NumpyArray]):
197
197
  id INTEGER PRIMARY KEY AUTOINCREMENT,
198
198
  text TEXT NOT NULL UNIQUE,
199
199
  vector BLOB NOT NULL
200
- )
200
+ ) STRICT
201
201
  """)
202
202
  con.commit()
203
203
 
@@ -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