Metadata-Version: 2.4
Name: tketool.llm
Version: 1.3.5
Summary: OpenAI-compatible model, prompt, structured invocation, embedding, and memory APIs for tketool
Author-email: Ke <jiangke1207@icloud.com>
License-Expression: MIT
Project-URL: Homepage, https://pypi.org/project/tketool.llm/
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: tketool.core==1.3.5
Requires-Dist: tketool.storage==1.3.5
Requires-Dist: openai<4,>=3
Requires-Dist: httpx<1,>=0.27
Requires-Dist: langchain-core<1.7,>=1.6
Requires-Dist: pydantic<3,>=2.10
Provides-Extra: local-embeddings
Requires-Dist: torch<3,>=2.4; extra == "local-embeddings"
Requires-Dist: transformers<6,>=5; extra == "local-embeddings"
Provides-Extra: test
Requires-Dist: pytest<9,>=8; extra == "test"

# tketool.llm

OpenAI-compatible model access, structured-output prompts, embeddings, and
memory.

```bash
pip install tketool.llm
```

## Chat Completions

```python
import os

from tketool.llm import OpenAIChatModel

llm = OpenAIChatModel(
    model_name="gpt-4o-mini",
    apitoken=os.environ["OPENAI_API_KEY"],
    base_url="https://api.openai.com/v1",
    call_dict={"temperature": 0.2},
)

text = llm("用三句话解释向量检索", return_detail=False)
print(text)
```

## Responses API

```python
import os

from tketool.llm import OpenAIResponsesModel

llm = OpenAIResponsesModel(
    model_name="gpt-5-mini",
    apitoken=os.environ["OPENAI_API_KEY"],
    base_url="https://api.openai.com/v1",
    call_dict={"max_output_tokens": 300},
)

text, detail = llm("给出一个最小 RAG 流程", return_detail=True)
print(text)
print(detail)
```

Both transports accept a complete `messages` list and return the same detail
shape. OpenAI-compatible gateways are supported by changing `base_url` and
`model_name`.

```python
messages = [
    {"role": "system", "content": "回答要简洁。"},
    {"role": "user", "content": "什么是结构化输出？"},
]

answer = llm("", return_detail=False, messages=messages)
```

The only public package path is `tketool.llm`; the retired `tketool.lmc`
namespace and LMC-prefixed type aliases are not shipped.

Local Hugging Face embeddings are optional:

```bash
pip install "tketool.llm[local-embeddings]"
```

## Memory

Memory is a small API backed by `tketool.storage`. Bind one instance to one user,
agent, or project space, then use `remember` and `recall`:

```python
from tketool.llm.memory import create_memory
from tketool.storage import MemoryBackend

storage = MemoryBackend()
memory = create_memory(storage=storage, space="users/user-001")

saved = memory.remember(
    "用户喜欢喝乌龙茶",
    kind="preference",
    tags=["profile", "drink"],
    metadata={"source": "chat"},
    idempotency_key="conversation-42/preference-1",
)

for item in memory.recall("用户喜欢喝什么？", limit=3):
    print(item.content, item.score)

memory.update(saved.id, tags=["profile", "confirmed"], if_revision=saved.revision)
memory.forget(saved.id)  # soft delete; pass hard=True for physical deletion
storage.close()
```

Choose persistence when constructing the storage backend; the memory API does not
change:

```python
import os

from tketool.storage import SQLiteBackend, create_backend

sqlite_storage = SQLiteBackend("memory.db")
postgres_storage = create_backend(os.environ["DATABASE_URL"])
# DATABASE_URL=postgresql+psycopg://user:password@localhost/app
```

Install the PostgreSQL driver with `pip install "tketool.storage[postgresql]"`.
`MemoryBackend` is process-local, SQLite is file-backed, and PostgreSQL uses
the `tketool.storage` SQLAlchemy adapter.

Lexical retrieval is available by default. Semantic and entity channels are
loaded only when their small protocols are injected:

```python
memory = create_memory(
    storage=storage,
    space="users/user-001",
    embedder=my_embedder,                 # implements embed(text) -> list[float]
    entity_extractor=my_entity_extractor, # implements extract(text) -> Iterable[str]
)

semantic = memory.recall("饮品偏好", using=["semantic"])
hybrid = memory.recall("Alice 的偏好", using=["lexical", "semantic", "entity"])
memory.reindex(using=["semantic"])  # after changing the embedding model/version
```

The built-in OpenAI-compatible embedding provider and tokenizer implement
those protocols directly:

```python
import os

from tketool.llm import OpenAIEmbeddingProvider
from tketool.llm.memory import SimpleTokenizer, create_memory
from tketool.storage import MemoryBackend

storage = MemoryBackend()
memory = create_memory(
    storage=storage,
    tokenizer=SimpleTokenizer(),
    embedder=OpenAIEmbeddingProvider(
        model_name="text-embedding-3-small",
        apitoken=os.environ["OPENAI_API_KEY"],
        base_url="https://api.openai.com/v1",
    ),
)

memory.remember("用户喜欢喝乌龙茶")
print(memory.recall("饮品偏好", using=["semantic"]))
```

For an offline local model, use `LocalTransformerEmbeddingProvider` with
`local_files_only=True`. It resolves a cached Hugging Face snapshot without a
network probe. Entity extraction remains application-specific: inject any
object implementing `extract(text) -> Iterable[str]`.

See `tketool.llm.memory.examples` for runnable memory, SQLite, and PostgreSQL
examples. The legacy `agent`/`context` implementation was removed because it
depended on the retired scheduler. Agent runtime code now lives under
`tools/agent_framework` and is not part of the `tketool.llm` distribution.
