kiarina-agi-base
Dependencies
| Package | Version | License |
|---|---|---|
| jaxtyping | >=0.3.3 |
MIT |
| kiarina-currency | >=2.3.1 |
MIT |
| kiarina-i18n | >=2.3.1 |
MIT |
| kiarina-utils-app | >=2.3.0 |
MIT |
| kiarina-utils-common | >=2.3.0 |
MIT |
| kiarina-utils-file | >=2.3.1 |
MIT |
| NumPy | >=2.0 |
BSD-3-Clause |
| Pydantic | >=2.11.7 |
MIT |
| pydantic-settings | >=2.10.1 |
MIT |
| pydantic-settings-manager | >=3.2.0 |
MIT |
| PyYAML | >=6.0.2 |
MIT |
| tiktoken | >=0.13.0 |
MIT |
| ulid-py | >=1.1.0 |
Apache-2.0 |
Installation
pip install kiarina-agi-base
Features
- Run contexts Keep application, organization, user, agent, node, locale, and metadata values together for each run.
- Cost recording Aggregate costs in memory, display them in the console, or save them to a local JSON Lines file.
- Request logging Write successful and failed request content to the console or local Markdown files.
- Token estimation Estimate token counts for text, images, audio, video, and PDFs.
- Formatting and media types Format console output, costs, and file references, and annotate image arrays.
Run Context
Set the required identifiers through environment variables before creating a RunContext.
export KIARINA_AGI_RUN_CONTEXT_ORGANIZATION_ID=my-org
export KIARINA_AGI_RUN_CONTEXT_USER_ID=my-user
export KIARINA_AGI_RUN_CONTEXT_AGENT_ID=my-agent
export KIARINA_AGI_RUN_CONTEXT_NODE_ID=my-node
export KIARINA_AGI_RUN_CONTEXT_DISALLOW_DEFAULT_IDS=true
export KIARINA_AGI_RUN_CONTEXT_TIMEZONE=Asia/Tokyo
export KIARINA_AGI_RUN_CONTEXT_LANGUAGE=ja
export KIARINA_AGI_RUN_CONTEXT_CURRENCY=JPY
from kiarina.agi.run_context import RunContext
from kiarina.utils.app import configure
configure("example-app", "example-author")
run_context = RunContext().with_metadata(request_id="req-123")
print(run_context.zone_info)
Cost Recording
Registries resolve implementations from a preset name or a name?key=value specifier. The local recorder writes to costs.jsonl in the user data directory.
import asyncio
from kiarina.agi.cost_record import CostRecord
from kiarina.agi.cost_recorder import cost_recorder_registry
from kiarina.agi.run_context import RunContext
from kiarina.utils.app import configure
async def main() -> None:
configure("example-app", "example-author")
recorder = cost_recorder_registry.resolve("local")
recorder.add(
CostRecord(
microdollars=125_000,
kind="chat",
source="example-model",
)
)
await recorder.flush(RunContext())
asyncio.run(main())
Available cost logger presets are console and null. Available cost recorder presets are local and null. Both default to null.
Request Logging
Available presets are console, local, and null. The local logger writes Markdown files below the user cache directory.
import asyncio
from kiarina.agi.request_logger import (
RequestLogEntry,
request_logger_registry,
)
from kiarina.agi.run_context import RunContext
from kiarina.utils.app import configure
async def main() -> None:
configure("example-app", "example-author")
logger = request_logger_registry.resolve("console")
await logger.log_request_success(
RequestLogEntry(
kind="chat",
source="example-model",
content="# Request\n\nHello",
),
run_context=RunContext(),
)
asyncio.run(main())
Token Estimation
Estimates vary by model and provider. Text uses TokenUtilsSettings.tiktoken_model_name, images use OpenAI tile calculations, and audio and video use duration-based factors.
from kiarina.agi.token_utils import (
ImageSize,
calc_image_token,
calc_text_token,
)
text_tokens = calc_text_token("Hello, world!")
image_tokens = calc_image_token(ImageSize(width=1024, height=768))
API Reference
kiarina.agi.run_context
from kiarina.agi.run_context import (
IDStr,
RunContext,
RunContextSettings,
TimeZone,
settings_manager,
)
RunContext
class RunContext(BaseModel):
app_author: str = <configured app author>
app_name: str = <configured app name>
organization_id: IDStr = <configured organization ID>
user_id: IDStr = <configured user ID>
agent_id: IDStr = <configured agent ID>
runner_id: IDStr = <generated ULID>
timezone: TimeZone = "UTC"
language: Language = "en"
currency: CurrencyCode = "USD"
metadata: dict[str, Any] = {}
@property
def zone_info(self) -> ZoneInfo: ...
def with_metadata(self, **kwargs: Any) -> Self: ...
runner_id identifies the runner that executes the agent. It is not read from settings; each RunContext without an explicit value gets a new ULID.
with_metadata returns a copy with the existing metadata updated.
RunContextSettings
class RunContextSettings(BaseSettings):
organization_id: IDStr | None = "default"
user_id: IDStr | None = "default"
agent_id: IDStr | None = "default"
disallow_default_ids: bool = False
timezone: TimeZone = "UTC"
language: Language = "en"
currency: CurrencyCode = "USD"
The environment variable prefix is KIARINA_AGI_RUN_CONTEXT_. Creating a RunContext raises ValueError when an identifier is not set, or when disallow_default_ids is True and an identifier is default. Enable this flag in multi-user environments to require explicit identifiers.
IDStr
IDStr = Annotated[
str,
StringConstraints(min_length=1, pattern=r"^[a-zA-Z0-9._-]+$"),
]
TimeZone
TimeZone: TypeAlias = str
An IANA time zone name.
settings_manager
settings_manager: SettingsManager[RunContextSettings]
kiarina.agi.cost_record
from kiarina.agi.cost_record import (
CostKind,
CostRecord,
CostSource,
Microdollars,
)
CostRecord
class CostRecord(BaseModel):
microdollars: Microdollars = 0
kind: CostKind
source: CostSource
metadata: dict[str, Any] = {}
created_at: datetime = <current UTC time>
Supporting types
CostKind = Literal[
"asr",
"chat",
"deep_research",
"image",
"text_embedding",
"image_embedding",
"tts",
"video",
"web_search",
] | str
CostSource: TypeAlias = str
Microdollars: TypeAlias = int
kiarina.agi.cost_logger
from kiarina.agi.cost_logger import (
BaseCostLogger,
CostLogger,
CostLoggerName,
CostLoggerSettings,
CostLoggerSpecifier,
cost_logger_registry,
settings_manager,
)
BaseCostLogger
class BaseCostLogger(CostLogger):
def __init__(self) -> None: ...
@property
def name(self) -> CostLoggerName: ...
@name.setter
def name(self, value: CostLoggerName) -> None: ...
@property
def currency(self) -> CurrencyCode | None: ...
@property
def exchange_rate(self) -> float | None: ...
@property
def decimal_places(self) -> int | None: ...
def log_cost_add(self, cost_record: CostRecord) -> None: ...
def log_cost_flush(self, cost_records: list[CostRecord]) -> None: ...
The base class for custom logger implementations.
CostLogger
@runtime_checkable
class CostLogger(Protocol):
name: CostLoggerName
def log_cost_add(self, cost_record: CostRecord) -> None: ...
def log_cost_flush(self, cost_records: list[CostRecord]) -> None: ...
CostLoggerSettings
class CostLoggerSettings(BaseSettings):
default: CostLoggerSpecifier = "null"
presets: dict[CostLoggerName, ImportPath] = {
"console": "kiarina.agi.cost_logger_impl.console:ConsoleCostLogger",
"null": "kiarina.agi.cost_logger_impl.null:NullCostLogger",
}
customs: dict[CostLoggerName, ImportPath] = {}
currency: CurrencyCode | None = None
exchange_rate: float | None = None
decimal_places: int | None = None
The environment variable prefix is KIARINA_AGI_COST_LOGGER_.
Supporting types and instances
CostLoggerName: TypeAlias = str
CostLoggerSpecifier: TypeAlias = CostLoggerName | str
cost_logger_registry: ComponentRegistry[CostLogger]
settings_manager: SettingsManager[CostLoggerSettings]
Specifiers also accept the "{CostLoggerName}?{ConfigString}" form.
kiarina.agi.cost_logger_impl.console
from kiarina.agi.cost_logger_impl.console import ConsoleCostLogger
ConsoleCostLogger
class ConsoleCostLogger(BaseCostLogger):
def __init__(self) -> None: ...
def log_cost_add(self, cost_record: CostRecord) -> None: ...
def log_cost_flush(self, cost_records: list[CostRecord]) -> None: ...
kiarina.agi.cost_logger_impl.null
from kiarina.agi.cost_logger_impl.null import NullCostLogger
NullCostLogger
class NullCostLogger(BaseCostLogger):
def __init__(self) -> None: ...
kiarina.agi.cost_recorder
from kiarina.agi.cost_recorder import (
BaseCostRecorder,
CostRecorder,
CostRecorderName,
CostRecorderSettings,
CostRecorderSpecifier,
cost_recorder_registry,
settings_manager,
)
BaseCostRecorder
class BaseCostRecorder(CostRecorder):
def __init__(self, **kwargs: Any) -> None: ...
@property
def name(self) -> CostRecorderName: ...
@name.setter
def name(self, value: CostRecorderName) -> None: ...
@property
def total_microdollars(self) -> int: ...
@property
def total_dollars(self) -> float: ...
@property
def logger(self) -> CostLogger: ...
def add(self, cost_record: CostRecord) -> None: ...
def clear(self) -> None: ...
async def flush(self, run_context: RunContext) -> None: ...
flush saves records, notifies the cost logger, and then removes the retained records.
CostRecorder
@runtime_checkable
class CostRecorder(Protocol):
name: CostRecorderName
records: list[CostRecord]
@property
def total_microdollars(self) -> int: ...
@property
def total_dollars(self) -> float: ...
def add(self, cost_record: CostRecord) -> None: ...
def clear(self) -> None: ...
async def flush(self, run_context: RunContext) -> None: ...
CostRecorderSettings
class CostRecorderSettings(BaseSettings):
default: CostRecorderSpecifier = "null"
presets: dict[CostRecorderName, ImportPath] = {
"local": "kiarina.agi.cost_recorder_impl.local:LocalCostRecorder",
"null": "kiarina.agi.cost_recorder_impl.null:NullCostRecorder",
}
customs: dict[CostRecorderName, ImportPath] = {}
The environment variable prefix is KIARINA_AGI_COST_RECORDER_.
Supporting types and instances
CostRecorderName: TypeAlias = str
CostRecorderSpecifier: TypeAlias = CostRecorderName | str
cost_recorder_registry: ComponentRegistry[CostRecorder]
settings_manager: SettingsManager[CostRecorderSettings]
Specifiers also accept the "{CostRecorderName}?{ConfigString}" form.
kiarina.agi.cost_recorder_impl.local
from kiarina.agi.cost_recorder_impl.local import LocalCostRecorder
LocalCostRecorder
class LocalCostRecorder(BaseCostRecorder):
def __init__(self, **kwargs: Any) -> None: ...
@property
def file_path(self) -> str: ...
kiarina.agi.cost_recorder_impl.null
from kiarina.agi.cost_recorder_impl.null import NullCostRecorder
NullCostRecorder
class NullCostRecorder(BaseCostRecorder):
def __init__(self, **kwargs: Any) -> None: ...
kiarina.agi.request_logger
from kiarina.agi.request_logger import (
BaseRequestLogger,
RequestLogEntry,
RequestLogger,
RequestLoggerName,
RequestLoggerSettings,
RequestLoggerSpecifier,
request_logger_registry,
settings_manager,
)
BaseRequestLogger
class BaseRequestLogger(RequestLogger):
def __init__(self, **kwargs: Any) -> None: ...
@property
def name(self) -> RequestLoggerName: ...
@name.setter
def name(self, value: RequestLoggerName) -> None: ...
async def log_request_success(
self,
log_entry: RequestLogEntry,
*,
run_context: RunContext,
) -> None: ...
async def log_request_error(
self,
log_entry: RequestLogEntry,
error: Exception,
*,
run_context: RunContext,
) -> None: ...
RequestLogEntry
class RequestLogEntry(BaseModel):
kind: str
source: str
content: str
metadata: dict[str, Any] = {}
created_at: datetime = <current UTC time>
RequestLogger
@runtime_checkable
class RequestLogger(Protocol):
name: RequestLoggerName
async def log_request_success(
self,
log_entry: RequestLogEntry,
*,
run_context: RunContext,
) -> None: ...
async def log_request_error(
self,
log_entry: RequestLogEntry,
error: Exception,
*,
run_context: RunContext,
) -> None: ...
RequestLoggerSettings
class RequestLoggerSettings(BaseSettings):
default: RequestLoggerSpecifier = "null"
presets: dict[RequestLoggerName, ImportPath] = {
"console": "kiarina.agi.request_logger_impl.console:ConsoleRequestLogger",
"local": "kiarina.agi.request_logger_impl.local:LocalRequestLogger",
"null": "kiarina.agi.request_logger_impl.null:NullRequestLogger",
}
customs: dict[RequestLoggerName, ImportPath] = {}
The environment variable prefix is KIARINA_AGI_REQUEST_LOGGER_.
Supporting types and instances
RequestLoggerName: TypeAlias = str
RequestLoggerSpecifier: TypeAlias = RequestLoggerName | str
request_logger_registry: ComponentRegistry[RequestLogger]
settings_manager: SettingsManager[RequestLoggerSettings]
Specifiers also accept the "{RequestLoggerName}?{ConfigString}" form.
kiarina.agi.request_logger_impl.console
from kiarina.agi.request_logger_impl.console import ConsoleRequestLogger
ConsoleRequestLogger
class ConsoleRequestLogger(BaseRequestLogger):
def __init__(self, **kwargs: Any) -> None: ...
async def log_request_success(
self,
log_entry: RequestLogEntry,
*,
run_context: RunContext,
) -> None: ...
async def log_request_error(
self,
log_entry: RequestLogEntry,
error: Exception,
*,
run_context: RunContext,
) -> None: ...
kiarina.agi.request_logger_impl.local
from kiarina.agi.request_logger_impl.local import LocalRequestLogger
LocalRequestLogger
class LocalRequestLogger(BaseRequestLogger):
def __init__(self, **kwargs: Any) -> None: ...
async def log_request_success(
self,
log_entry: RequestLogEntry,
*,
run_context: RunContext,
) -> None: ...
async def log_request_error(
self,
log_entry: RequestLogEntry,
error: Exception,
*,
run_context: RunContext,
) -> None: ...
kiarina.agi.request_logger_impl.null
from kiarina.agi.request_logger_impl.null import NullRequestLogger
NullRequestLogger
class NullRequestLogger(BaseRequestLogger):
def __init__(self, **kwargs: Any) -> None: ...
kiarina.agi.token_utils
from kiarina.agi.token_utils import (
ImageSize,
TokenCount,
TokenUtilsSettings,
calc_audio_token,
calc_image_token,
calc_pdf_token,
calc_text_token,
calc_video_token,
settings_manager,
)
Token calculation
def calc_audio_token(duration: float) -> TokenCount: ...
def calc_image_token(image_size: ImageSize) -> TokenCount: ...
def calc_pdf_token(text: str, image_sizes: list[ImageSize]) -> int: ...
def calc_text_token(text: str) -> TokenCount: ...
def calc_video_token(duration: float) -> TokenCount: ...
duration is measured in seconds. calc_pdf_token adds the text estimate and the estimates for each page image.
TokenUtilsSettings
class TokenUtilsSettings(BaseSettings):
tiktoken_model_name: str = "gpt-4o"
The environment variable prefix is KIARINA_AGI_TOKEN_UTILS_.
Supporting types and instances
class ImageSize(NamedTuple):
width: int
height: int
TokenCount: TypeAlias = int
settings_manager: SettingsManager[TokenUtilsSettings]
kiarina.agi.console_utils
from kiarina.agi.console_utils import (
ConsoleColor,
divider,
format_run_context,
section_header,
stderr_color,
)
Console formatting
def divider(width: int = 80, fill_char: str = "-") -> str: ...
def format_run_context(run_context: RunContext) -> str: ...
def section_header(
title: str,
*,
width: int = 80,
fill_char: str = "-",
) -> str: ...
@contextmanager
def stderr_color(color: ConsoleColor) -> Iterator[None]: ...
stderr_color applies ANSI colors only when stderr is a TTY.
ConsoleColor
ConsoleColor = Literal[
"black",
"red",
"green",
"yellow",
"blue",
"magenta",
"cyan",
"white",
]
kiarina.agi.cost_utils
from kiarina.agi.cost_utils import format_cost
format_cost
def format_cost(
microdollars: int,
*,
currency: CurrencyCode | None = None,
exchange_rate: float | None = None,
decimal_places: int | None = None,
) -> str: ...
Converts microdollars to a string with a currency. Pass both currency and exchange_rate to convert from USD.
kiarina.agi.file_utils
from kiarina.agi.file_utils import (
format_xml_attributes,
is_uri,
normalize_line_number,
normalize_page,
normalize_time,
)
File reference formatting
def format_xml_attributes(xml_attributes: dict[str, Any]) -> str: ...
def is_uri(s: str) -> bool: ...
def normalize_line_number(line_number: int, line_count: int) -> int: ...
def normalize_page(page_number: int, page_count: int) -> int: ...
def normalize_time(time: float, duration: float) -> float: ...
normalize_line_number and normalize_page clamp one-based values to valid ranges. normalize_time clamps a value in seconds between 0 and duration.
kiarina.agi.image_types
from kiarina.agi.image_types import ImagePixels
ImagePixels
ImagePixels: TypeAlias = UInt8[np.ndarray, "height width rgb"]
Represents a uint8 RGB image array.
Metadata
Release files for kiarina-agi-base 2.34.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| kiarina_agi_base-2.34.0.tar.gz | 21.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| kiarina_agi_base-2.34.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 59.0 kB
Release files / kiarina_agi_base-2.34.0.tar.gz
| Download URL | kiarina_agi_base-2.34.0.tar.gz |
|---|---|
| Size | 21.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f9ae4a7dd5eec7f6d8826790cdcdcae678cd5817b9fea981cb1effc0dfb67abf
|
|
BLAKE2b-256 checksum How to use checksums |
45916b59fcb888876a45b1f2f84e12728619792a84e66c9b9341ccddc2f68e00
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency logRelease files / kiarina_agi_base-2.34.0-py3-none-any.whl
| Download URL | kiarina_agi_base-2.34.0-py3-none-any.whl |
|---|---|
| Size | 37.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
819d891858fa3e4d51ef188f3581d2897ca2b6effa57c8a81a6810d29c596ebd
|
|
BLAKE2b-256 checksum How to use checksums |
947aa0dd7977001a6897475f86e6d5a250e148339385904ed5297f890a3ea198
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency log