qfa.domain.ports#
Port interfaces (protocols) for the feedback analysis backend.
Driven ports declared here use typing.Protocol for structural
subtyping per ADR-002. Each application service in qfa.services is
exposed as its own concrete class per ADR-011 and ADR-017 (no driving
port).
Classes
|
Port for anonymising and de-anonymising user-supplied text. |
|
Port for authenticating users of the application. |
|
Port for adding/ removing keys and tenants from the application. |
|
Port for a multilingual text-embedding model. |
|
Port for interacting with a large-language-model provider. |
|
Port for recording and querying LLM usage data. |
- class qfa.domain.ports.EmbeddingPort(*args, **kwargs)[source]#
Bases:
ProtocolPort for a multilingual text-embedding model.
Implementations MUST be multilingual: community feedback is multilingual, and a monolingual model would cluster by language instead of theme.
embedreturns one dense vector per input text, in input order.Synchronous by design: encoding is CPU-bound local computation, not an I/O call (contrast
LLMPort.complete()). If a future externalised adapter makes embedding I/O-bound, an async variant can be added then.
- class qfa.domain.ports.LLMPort(*args, **kwargs)[source]#
Bases:
ProtocolPort for interacting with a large-language-model provider.
Implementations must translate provider-specific details into the domain
LLMResponsemodel.- async complete(system_message: str, user_message: str, tenant_id: str, response_model: type[T_Response], timeout: float = 20.0) LLMResponse[source]#
Send a completion request to the LLM provider.
- Parameters:
system_message (str) – The system-level instruction for the model.
user_message (str) – The user-level message to complete.
tenant_id (str) – Tenant identifier for tracking and billing.
response_model (type[T_Response]) – The Pydantic model to parse the response into.
timeout (float) – Maximum time in seconds to wait for a response.
- Returns:
The model’s response including token usage.
- Return type:
- class qfa.domain.ports.UsageRepositoryPort(*args, **kwargs)[source]#
Bases:
ProtocolPort for recording and querying LLM usage data.
- async record_call(record: LLMCallRecord) None[source]#
Record a single LLM call attempt.
- Parameters:
record (LLMCallRecord) – The call record to persist.
- async get_usage_stats_for_one_tenant(tenant_id: str, from_: datetime | None = None, to: datetime | None = None) TenantUsageStats[source]#
Get aggregated usage stats for a single tenant.
- Parameters:
tenant_id (str) – The tenant to query.
from (datetime | None) – Inclusive lower bound (UTC tz-aware), or None.
to (datetime | None) – Exclusive upper bound (UTC tz-aware), or None.
- Returns:
Stats for the tenant. When no calls match the window, a zero-valued
TenantUsageStatsis returned (never None).- Return type:
- async get_all_usage_by_tenant(from_: datetime | None = None, to: datetime | None = None) list[TenantUsageStats][source]#
Get per-tenant stats plus a grand total entry (tenant_id=None).
- Parameters:
from (datetime | None) – Inclusive lower bound (UTC tz-aware), or None.
to (datetime | None) – Exclusive upper bound (UTC tz-aware), or None.
- Returns:
Per-tenant stats followed by a grand total entry.
- Return type:
- async get_all_usage_by_operation(from_: datetime | None = None, to: datetime | None = None) list[OperationUsageStats][source]#
Get per-operation stats with nested per-tenant breakdown plus grand total.
Inverse hierarchy of
get_all_usage_by_tenant(): top-level aggregation is by orchestrator operation; each operation block carries a tuple of per-tenant blocks. The grand-total entry (operation=None) is always emitted last, matching the convention used byget_all_usage_by_tenant().- Parameters:
from (datetime | None) – Inclusive lower bound (UTC tz-aware), or None.
to (datetime | None) – Exclusive upper bound (UTC tz-aware), or None.
- Returns:
Per-operation stats followed by a grand total entry.
- Return type:
- class qfa.domain.ports.AnonymizationPort(*args, **kwargs)[source]#
Bases:
ProtocolPort for anonymising and de-anonymising user-supplied text.
Implementations replace named entities (people, locations, phone numbers, etc.) in
textwith stable placeholders, returning the redacted text together with a mapping that can be used to restore the original values viadeanonymize.Implementations must be deterministic for a given input within a single call (same entity replaced by the same placeholder).
- anonymize(text: str) tuple[str, dict[str, str]][source]#
Replace sensitive entities in
textwith placeholders.
- class qfa.domain.ports.AuthLookupPort(*args, **kwargs)[source]#
Bases:
ProtocolPort for authenticating users of the application.
- async validate_api_key(provided_key: str) TenantApiKey | None[source]#
Validate if a key exists in the implemented adapter.
- Parameters:
provided_key (str) – The API key value supplied by the caller.
- Returns:
The matching tenant API key, or None if no match was found.
- Return type:
TenantApiKey | None
- class qfa.domain.ports.AuthManagementPort(*args, **kwargs)[source]#
Bases:
ProtocolPort for adding/ removing keys and tenants from the application.
- async add_tenant(tenant_name: str, allows_superusers: bool = False) str[source]#
Add a new tenant to the implemented adapter and return its unique identifier.
- async delete_tenant(tenant_id: str) None[source]#
Delete an existing tenant from the implemented adapter.
- Parameters:
tenant_id (str) – The unique identifier of the tenant to delete.
- Raises:
TenantNotFoundError: – If no tenant with this tenant_id exists
- async add_key(key_name: str, tenant_id: str, is_superuser: bool = False) KeyCreationResponse[source]#
Generate and persist a new API key in the implemented adapter.
- Parameters:
- Returns:
The created key identifier and plaintext API key as
(key_id, api_key). The plaintext key is returned only once at creation time.- Return type:
- Raises:
KeyAlreadyExistsError: – If key with this key_id already exists
TenantDoesNotAllowSuperUsersError: – If the tenant does not allow superuser keys and is_superuser is True
- async delete_key(key_id: str) None[source]#
Delete an existing API key from the implemented adapter.
- Parameters:
key_id (str) – The unique identifier of the API key record to remove.
- async get_tenants() list[TenantInfo][source]#
Return metadata for all tenants in the implemented adapter.
- Returns:
A list of TenantInfo objects with tenant metadata.
- Return type: