Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: CI

# Run on every pull request, and on pushes to main (so merges are verified too).
on:
pull_request:
push:
branches: [main]

# Only need read access to check out the code.
permissions:
contents: read

# Cancel superseded runs for the same ref (e.g. new pushes to an open PR).
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Match the versions in pyproject's classifiers (requires-python >=3.11).
python-version: ["3.11", "3.12"]

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Install uv and Python ${{ matrix.python-version }}
uses: astral-sh/setup-uv@v6
with:
python-version: ${{ matrix.python-version }}
enable-cache: true

- name: Install dependencies
run: uv sync

- name: Lint (ruff)
run: uv run --with ruff ruff check src tests

- name: Type-check (mypy)
run: uv run --with mypy mypy src tests

- name: Test (pytest)
run: uv run pytest
10 changes: 10 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,13 @@ Issues = "https://github.com/folathecoder/agentling/issues"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]

[dependency-groups]
dev = [
"pytest>=9.1.1",
"pytest-asyncio>=1.4.0",
]
18 changes: 16 additions & 2 deletions src/agentling/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
"""Provider-neutral model layer and the OpenAI-compatible adapter.

This module defines the framework's message types (ChatMessage, ToolCall,
Usage) and streaming types (Delta, ToolCallDelta), the Model protocol that the
agent loop depends on, and OpenAIModel — an async adapter for any
OpenAI-compatible chat-completions endpoint, with rate-limit retries,
streaming, and token-usage capture.
"""

from __future__ import annotations

import asyncio
Expand Down Expand Up @@ -59,6 +68,7 @@ class Usage:

@property
def total_tokens(self) -> int:
"""Total tokens consumed (input + output)."""
return self.input_tokens + self.output_tokens


Expand All @@ -85,13 +95,17 @@ async def generate(
self,
messages: Sequence[ChatMessage],
tools: Sequence[ToolSpec] | None = None,
) -> ChatMessage: ...
) -> ChatMessage:
"""Generate one assistant response for the conversation so far."""
...

def stream(
self,
messages: Sequence[ChatMessage],
tools: Sequence[ToolSpec] | None = None,
) -> AsyncIterator[Delta]: ...
) -> AsyncIterator[Delta]:
"""Stream the assistant response as incremental deltas."""
...


class OpenAIModel:
Expand Down
Loading
Loading