[Enhancement] Extend the DI container
Motivation
Some classes are not managed by the Lagom DI container and are instead instantiated via hardcoded imports. This makes them difficult to customize without modifying or extending upstream code.
Problem Example
Consider a class A (managed by DI) that directly constructs an instance of class B (not managed by DI). Because the construction is hardcoded, replacing B with a custom implementation (e.g. B') requires subclassing A and overriding the method responsible for creating B.
Example from GenericGuidelineMatchingStrategy:
def _create_batch_observational_guideline(
self,
guidelines: Sequence[Guideline],
journeys: Sequence[Journey],
context: GuidelineMatchingContext,
) -> GenericObservationalGuidelineMatchingBatch:
return GenericObservationalGuidelineMatchingBatch(
logger=self._logger,
meter=self._meter,
optimization_policy=self._optimization_policy,
schematic_generator=self._observational_guideline_schematic_generator,
guidelines=guidelines,
journeys=journeys,
context=context,
)Here, GenericObservationalGuidelineMatchingBatch is instantiated directly. To replace it with a custom implementation (e.g. MyGenericObservationalGuidelineMatchingBatch), we must override the entire GenericGuidelineMatchingStrategy.
Classes That Currently Require Workarounds
The following classes require similar overrides or monkey patching:
Guideline matching batches (
Actionable,Observational,PreviouslyAppliedActionableCustomerDependent) We replace these to customize prompt construction (e.g. injecting shots with proper ASCII escaping for language diacritics). This requires overridingGenericGuidelineMatchingStrategyin DI.While Parlant supports prompt customization via named sections, some section names are reused across the codebase (e.g.
BuiltInSection.GUIDELINES), which creates ambiguity.GenericResponseAnalysisBatchSame motivation as above — deeper prompt customization than section replacement allows.PluginClientMonkey patched to propagate the current request’s trace ID across HTTP boundaries (engine → tool). Without this, spans created inside tools are attached to disconnected (rootless) traces.PromptBuilderMonkey patched to override_INTERACTION_BODYand_EMPTY_HISTORY.
Proposed Solution
Allow these classes to be managed and overridden via the DI container.
Discussion
Most of these issues (except PluginClient) stem from the need to modify every prompt sent to the model.
We tailor the engine for a very small LLM with limited instruction-following ability, trading capability for lower latency and energy consumption. This requires fine-grained control over prompt structure.
In many cases, replacing prompt sections by name is sufficient. However, some changes require deeper modifications, such as:
- adjusting response format instructions (e.g.
_format_of_guideline_check_json_descriptioninGenericResponseAnalysisBatch) - modifying how data is passed into templates
Additionally, some section names are reused in different contexts with different meanings (e.g. guideline-action-proposer-output-format). This makes section-level overrides unreliable and forces us to modify internal class logic instead of working at the abstraction level described in the docs.
Source: emcie-co/parlant