Migrating to 0.3.0
0.3.0 adds a stateless core: extractors return spans with their chunk ids and offsets, and a Transformer turns a question and chunks into a cited answer. VerbatimRAG is built on a Transformer you pass in. Models and LLM clients are passed into constructors, and the settings of a request are arguments of the call.
The old entry points keep working through the 0.3.x and 0.4.x releases. Each one warns with VerbatimDeprecationWarning, a DeprecationWarning, whose message names its replacement:
They are removed in 0.5.0.
Finding the old calls
Python hides DeprecationWarnings outside __main__. Turn the library's warnings into errors in your tests to find every old call:
# pyproject.toml
[tool.pytest.ini_options]
filterwarnings = ["error::verbatim_core.VerbatimDeprecationWarning"]
Or show them while running a script: python -W always::DeprecationWarning your_script.py. Importing either package never warns; using an old name does, including importing a name from a moved module such as verbatim_rag.transform.
Answering from chunks
VerbatimTransform took context dicts and returned a QueryResponse. Build a Transformer from an extractor and templates instead, pass it chunks, and turn its Answer into a QueryResponse when you need one:
# Before
from verbatim_core import VerbatimTransform
vt = VerbatimTransform(template_mode="contextual", max_display_spans=5)
response = vt.transform(question, context)
# After
from verbatim_core import Chunk, TransformConfig, Transformer
from verbatim_core.extractors import LLMSpanExtractor
from verbatim_core.llm_client import LLMClient
from verbatim_core.response_builder import ResponseBuilder
from verbatim_core.templates import TemplateManager
client = LLMClient("gpt-4o-mini")
transformer = Transformer(
LLMSpanExtractor(client),
TemplateManager(llm_client=client, default_mode="contextual"),
config=TransformConfig(max_display_spans=5),
)
chunks = [
Chunk(id=f"ctx_{i}", text=item["content"], metadata={"title": item.get("title", "")})
for i, item in enumerate(context)
]
answer = transformer.transform(question, chunks)
response = ResponseBuilder().build_from_answer(question, answer, chunks)
A chunk's id is yours to choose, unique within a call; the answer's citations point at it with offsets into its text.
| Old | New |
|---|---|
VerbatimTransform(...), transform, transform_async |
Transformer(extractor, templates, config=TransformConfig(...)), transform, atransform, then ResponseBuilder().build_from_answer |
verbatim_query(provider, question, k) |
context = provider.retrieve(question, k=k), chunks from the context items as above, Transformer.transform, then ResponseBuilder().build_from_answer |
verbatim_query_async(provider, question, k) |
context = await provider.retrieve_async(question, k=k), chunks from the context items as above, Transformer.atransform, then ResponseBuilder().build_from_answer |
verbatim_rag.transform.VerbatimTransform, verbatim_query, verbatim_query_async |
the same names from verbatim_core, then the rows above |
verbatim_rag.transform.SearchResult |
verbatim_rag.vector_stores.SearchResult |
Extractors
extract takes chunks and returns an Extraction: spans with their chunk ids, offsets and scores, the usage of the call and its warnings. extract_spans returned span texts keyed by chunk text.
# Before
spans_by_text = extractor.extract_spans(question, search_results)
# After
extraction = extractor.extract(question, chunks)
for span in extraction.spans:
print(span.chunk_id, span.start, span.end, span.text)
| Old | New |
|---|---|
extractor.extract_spans(question, results) |
extractor.extract(question, chunks) |
extractor.extract_spans_async(question, results) |
await extractor.aextract(question, chunks) |
A SpanExtractor subclass that implements only extract_spans |
Implement extract(self, question, chunks, *, config=None), returning an Extraction. Until then, extract locates the span texts in their chunks and warns. |
A subclass of LLMSpanExtractor or another extractor that overrides extract_spans, for example to filter the spans |
Override extract instead, and filter the Extraction's spans. Until then, extract and aextract call your extract_spans and warn. |
LLMSpanExtractor(), LLMSpanExtractor(model="gpt-4o") |
LLMSpanExtractor(LLMClient("gpt-4o")) |
LLMSpanExtractor(client, model=...) |
The model of the client: LLMClient(model) |
LLMSpanExtractor(client, extraction_mode=..., batch_size=..., span_match_mode=..., fuzzy_threshold=..., extraction_prompt=..., system_prompt=...) |
LLMSpanExtractor(client, config=LLMExtractConfig(...)) with the same names |
LLMSpanExtractor(..., max_display_spans=...), ModelSpanExtractor(..., max_display_spans=...) |
Transformer(..., config=TransformConfig(max_display_spans=...)) |
ModelSpanExtractor(), ModelSpanExtractor("org/model"), ModelSpanExtractor(model_path=..., device=...) |
ModelSpanExtractor.from_pretrained("org/model", device=...) |
ModelSpanExtractor(..., threshold=..., min_span_chars=..., merge_gap_chars=..., max_length=..., doc_stride=...) |
config=ModelExtractConfig(...) with the same names |
ModelSpanExtractor(..., extraction_mode=...) |
Nothing in its place: it had no effect |
SemanticHighlightExtractor(), SemanticHighlightExtractor("org/model"), SemanticHighlightExtractor(model_name=..., device=..., max_tokens=...) |
SemanticHighlightExtractor.from_pretrained("org/model", device=..., max_tokens=...) |
SemanticHighlightExtractor(..., threshold=..., output_mode=..., language=..., min_span_tokens=..., merge_gap=...) |
config=SemanticHighlightConfig(...) with the same names |
from_pretrained is the one place a model name becomes a loaded model, so you decide when the download happens. A loaded model can also be passed in: ModelSpanExtractor(model).
Templates
The settings of a TemplateManager are its config, the default of every Transformer built with it. Pass other settings to the Transformer or to one call instead of changing the templates:
# Before
templates.set_mode("random")
templates.set_citation_mode("hidden")
# After: for every call of this transformer
transformer = Transformer(
extractor, templates, config=TransformConfig(template_mode="random", citation_mode="hidden")
)
# or for one call
answer = transformer.transform(question, chunks, config=TransformConfig(template_mode="random"))
| Old | New |
|---|---|
TemplateManager.set_mode(mode), assigning TemplateManager.current_mode |
TransformConfig(template_mode=mode); for structured mode, TemplateManager.use_structured_mode() |
TemplateManager.set_citation_mode(mode), assigning TemplateManager.citation_mode |
TransformConfig(citation_mode=mode) |
TemplateManager.set_citation_format(format) |
TransformConfig(citation_format=format) |
| Contextual template mode without an LLM client, which used the static template | TemplateManager(llm_client=client) |
TemplateManager.set_rag_system, StructuredTemplate.set_rag_system, StructuredTemplate.fill_async |
VerbatimRAG.query_async, which answers structured questions itself |
QuestionSpecificTemplate(model_name=..., device=...), question-specific templates without an embedder |
QuestionSpecificTemplate.from_pretrained(model_name, device, templates=...), or embedder= a loaded model, also in TemplateManager.use_question_specific_mode(templates, embedder=...) |
VerbatimRAG
VerbatimRAG takes the Transformer that writes its answers, and k is an argument of each query:
# Before
rag = VerbatimRAG(index, model="gpt-4o-mini", k=5, template_mode="contextual", max_display_spans=5)
response = rag.query(question)
# After
client = LLMClient("gpt-4o-mini")
transformer = Transformer(
LLMSpanExtractor(client),
TemplateManager(llm_client=client, default_mode="contextual"),
config=TransformConfig(max_display_spans=5),
)
rag = VerbatimRAG(index, transformer=transformer, llm_client=client)
response = rag.query(question, k=5)
The llm_client of VerbatimRAG serves structured template mode, which answers without the Transformer. Nothing is set on VerbatimRAG by a call, so one instance can serve concurrent queries.
| Old | New |
|---|---|
VerbatimRAG(index), and the arguments model, template_manager, extractor, max_display_spans, template_mode and extraction_mode |
VerbatimRAG(index, transformer=Transformer(extractor, templates, config=...), llm_client=client) |
VerbatimRAG(index, k=...) |
rag.query(question, k=...), query_async and query_stream likewise, and VerbatimDOC(rag, k=...) |
Assigning rag.extractor or rag.template_manager |
A VerbatimRAG with a Transformer of that extractor and those templates |
Assigning rag.max_display_spans |
Transformer(..., config=TransformConfig(max_display_spans=...)) |
StreamingRAG(rag).stream_query(question, num_docs, ...), stream_query_sync |
rag.query_stream(question, k=..., ...) |
verbatim_rag.document.Chunk |
verbatim_rag.document.DocumentChunk |
query_stream yields typed events instead of dicts, and raises instead of sending an error event:
# Before
async for event in StreamingRAG(rag).stream_query(question, num_docs=5):
if event["type"] == "documents":
show_sources(event["data"])
elif event["type"] == "answer":
show_answer(event["data"])
# After
async for event in rag.query_stream(question, k=5):
if event.type == "documents":
show_sources(event.chunks)
elif event.type == "spans":
highlight(event.spans)
else:
show_answer(event.response)
The last event carries the same QueryResponse that query returns for the same input.
What changes without a warning
Some behaviour changes in 0.3.0 for the old entry points too. The changelog has the details:
- A failed LLM call during extraction raises an
UpstreamErroror aConfigurationError, instead of answering from the remaining chunks (ADR 0006). - One span gives one highlight and one citation, where the span is, instead of one at every occurrence of its text (ADR 0001).
- Random template mode gives the same question the same template.
- A context item of the wrong shape raises
InvalidInputError, aValueError, instead ofTypeError:except TypeErrorno longer catches it. - A
QueryResponselists inwarningshow its answer is degraded but still correct, such as quotes that couldn't be located. - The steps of a call log at DEBUG, and customer text is logged only on the
verbatim_core.contentlogger. See Errors, warnings and logs.