Metadata-Version: 2.5
Name: determine
Version: 0.5.0
Summary: Simple context-aware AI inference for Python.
Author: Jack
License-Expression: MIT
License-File: LICENSE
Keywords: ai,decision,generation,inference,llm,procedural,reasoning
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# Determine

Determine makes AI inference feel like a normal part of Python.

The main API has only two operations:

~~~python
determine.answer(...)
determine.choose(...)
~~~

## Install

~~~bash
pip install determine
~~~

## Quick start

~~~python
import determine

determine.configure(
    "http://127.0.0.1:8080"
)

preference = "Chinese"
budget = "cheap"

answer = determine.answer(
    "What should I eat?"
)

print(answer)
~~~

Determine can automatically see relevant runtime variables and surrounding
source code.

# answer()

Use `answer()` when the result can be open-ended.

~~~python
response = determine.answer(
    "Explain what is happening."
)
~~~

# choose()

Use `choose()` when the result must be one of several possibilities.

~~~python
health = 12
ammo = 0
enemy_distance = 3

action = determine.choose(
    "What should the player do?",
    [
        "fight",
        "run",
        "hide"
    ]
)

print(action)
~~~

# Explicit context

Automatic context is convenient for small programs:

~~~python
determine.answer(
    "What should happen?"
)
~~~

For larger programs, you can specify exactly what Determine should see.

By variable name:

~~~python
health = 12
ammo = 0
score = 400

action = determine.choose(
    "What should the player do?",
    [
        "fight",
        "run",
        "hide"
    ],
    context=[
        "health",
        "ammo"
    ]
)
~~~

Only `health` and `ammo` are sent as runtime context.

You can also provide an explicit dictionary:

~~~python
answer = determine.answer(
    "What should the player do?",
    context={
        "health": health,
        "ammo": ammo
    }
)
~~~

Or disable automatic context entirely:

~~~python
answer = determine.answer(
    "Say hello.",
    context=False
)
~~~

This makes it possible to start with Determine's automatic context while
still having precise control when an application becomes larger.

# Typed structured answers

`answer()` can return multiple typed values at once by using `schema=`.

~~~python
result = determine.answer(
    "Choose an action and estimate your confidence.",
    schema={
        "action": str,
        "confidence": float
    }
)

print(result)
~~~

The result is a normal Python dictionary:

~~~python
{
    "action": "run",
    "confidence": 0.93
}
~~~

Supported types include:

~~~python
str
int
float
bool
list
dict
~~~

Nested structures also work:

~~~python
result = determine.answer(
    "Evaluate the current situation.",
    schema={
        "action": str,
        "confidence": float,
        "details": {
            "danger": int,
            "safe": bool
        }
    }
)
~~~

Determine validates and converts the returned values to the requested types.

# max_tokens

Both operations support a generation limit:

~~~python
answer = determine.answer(
    "Think carefully about this.",
    max_tokens=2048
)
~~~

~~~python
choice = determine.choose(
    "What should happen?",
    [
        "continue",
        "stop",
        "retry"
    ],
    max_tokens=1024
)
~~~

This can be useful with reasoning models that need generation space before
producing their final answer.

# Multi-turn conversations

Use a normal Python list:

~~~python
history = []
~~~

Then reuse it:

~~~python
print(
    determine.answer(
        "My spaceship is called Juniper.",
        history=history
    )
)

print(
    determine.answer(
        "What is my spaceship called?",
        history=history
    )
)
~~~

The same history can be shared between `answer()` and `choose()`.

# Functions as choices

Functions can also be options.

~~~python
def backup():
    """Back up the connected device."""
    print("Backing up...")


def update(version="latest"):
    """Update the connected device."""
    print("Updating to", version)


request = input("> ")

determine.choose(
    request,
    [
        backup,
        update
    ]
)
~~~

Determine can choose the function that genuinely matches the request and
extract its arguments.

If none of the functions can perform the request, it returns `None`.

# OpenAI-compatible APIs

~~~python
determine.configure(
    "http://127.0.0.1:8080"
)
~~~

Determine attempts to discover the model automatically.

You can also specify it:

~~~python
determine.configure(
    "http://127.0.0.1:8080",
    model="my-model"
)
~~~

# Ollama

~~~python
determine.configure(
    "ollama:qwen3"
)
~~~

Or:

~~~python
determine.configure(
    "ollama"
)
~~~

# llama.cpp

~~~python
determine.configure(
    "llama.cpp:/home/me/model.gguf"
)
~~~

Extra arguments can be supplied:

~~~python
determine.configure(
    "llama.cpp:/home/me/model.gguf",
    args=[
        "-ngl", "all",
        "-c", "32768"
    ]
)
~~~

# API keys

~~~bash
export DETERMINE_API_KEY="your-key"
~~~

Determine also checks `OPENAI_API_KEY`.

# Advanced configuration

~~~python
determine.configure(
    endpoint="http://localhost:8080",
    model="my-model",
    temperature=0.2,
    timeout=120,
    reasoning_effort="high"
)
~~~

# Why Determine?

Some decisions have many interacting variables, thresholds, and possible
combinations.

Instead of maintaining a large decision tree:

~~~python
action = determine.choose(
    "What should the enemy do?",
    [
        "attack",
        "defend",
        "run",
        "heal"
    ]
)
~~~

Determine can reason over the current program state.

Useful examples include:

- game AI
- procedural generation
- adaptive software
- natural-language tools
- hardware-aware settings
- recommendation systems
- fuzzy classification
- decisions involving many variables and thresholds

# Security

AI output is nondeterministic.

Do not use Determine as the only protection for authentication, permissions,
financial actions, destructive operations, security boundaries, or other
safety-critical systems.

Only expose functions to `choose()` that the AI should actually be allowed
to execute.

# License

MIT
