Metadata-Version: 2.4
Name: stigmem-plugin-gemini-adapter
Version: 0.1.0
Summary: Experimental Gemini FunctionDeclaration adapter for Stigmem.
Project-URL: Homepage, https://github.com/eidetic-labs/stigmem
Project-URL: Documentation, https://github.com/eidetic-labs/stigmem/tree/main/experimental/gemini-adapter
Project-URL: Repository, https://github.com/eidetic-labs/stigmem
Project-URL: Issues, https://github.com/eidetic-labs/stigmem/issues
Author-email: Eidetic Labs <oss@eidetic-labs.ai>
License: Apache-2.0
Keywords: adapter,function-calling,gemini,plugins,stigmem
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.11
Requires-Dist: pydantic<3,>=2
Requires-Dist: stigmem-node<1.0.0,>=0.9.0a10
Provides-Extra: gemini
Requires-Dist: google-generativeai<1.0,>=0.8; extra == 'gemini'
Provides-Extra: test
Requires-Dist: respx<1.0,>=0.20; extra == 'test'
Description-Content-Type: text/markdown

# stigmem-plugin-gemini-adapter

Integrates Stigmem with the [Google Gemini API](https://ai.google.dev) using
Gemini's native **FunctionDeclaration** format.

This package is experimental and opt-in. Installing it makes the
`gemini-adapter` plugin discoverable through the `stigmem.plugins` entry-point
group; host applications still choose when to call the adapter.

## Design

The adapter has three surfaces:

1. **`gemini_tools()`** — returns `STIGMEM_FUNCTION_DECLARATIONS`, a
   `list[dict]` of Google `FunctionDeclaration`-shaped JSON objects ready to
   pass to `genai.protos.FunctionDeclaration(**t)` or directly to the REST API.

2. **`dispatch(fn_name, fn_args)`** — executes a Stigmem tool call triggered
   by a Gemini `FunctionCall` part and returns a JSON string to wrap in a
   `FunctionResponse`.

3. **`run(system_prompt, user_message)`** — a thin agentic loop that handles
   the full tool-use turn sequence. Requires `google-generativeai` installed.

The adapter depends on Stigmem's Python client surface. The
`google-generativeai` SDK is an optional dependency needed only for `run()`.

## Files

| File | Purpose |
|---|---|
| `src/stigmem_plugin_gemini/adapter.py` | Adapter — function declarations + dispatch + agentic loop |
| `src/stigmem_plugin_gemini/manifest.py` | Stigmem plugin discovery manifest |
| `tests/conftest.py` | pytest path setup |
| `tests/test_adapter.py` | Unit tests (respx-mocked) |

## Installation

```bash
python -m pip install 'stigmem-plugin-gemini-adapter>=0.1.0,<2.0.0'
```

Install the Gemini SDK only in deployments that use the convenience
`run()` loop:

```bash
python -m pip install 'stigmem-plugin-gemini-adapter[gemini]>=0.1.0,<2.0.0'
```

### Requirements

- Python ≥ 3.11
- `stigmem-py`: `pip install stigmem-py` (or from workspace)
- `google-generativeai`: optional runtime extra for `run()`; function
  declarations, dispatch, unit tests, and plugin discovery do not require it.

### Environment variables

```bash
STIGMEM_URL=http://localhost:8765
STIGMEM_API_KEY=sk-your-key          # optional
STIGMEM_SOURCE_ENTITY=agent:gemini   # entity URI for this agent
STIGMEM_GEMINI_MODEL=gemini-2.0-flash  # model to use in run()
GOOGLE_API_KEY=your-gemini-key       # required for run()
```

## Usage

### Raw declarations (no Gemini SDK needed)

```python
from stigmem_plugin_gemini import STIGMEM_FUNCTION_DECLARATIONS

# Pass to any Gemini-compatible API as plain JSON
print(STIGMEM_FUNCTION_DECLARATIONS[0]["name"])  # → "assert_fact"
```

### With the Gemini SDK

```python
import google.generativeai as genai
from stigmem_plugin_gemini import StigmemGeminiAdapter

genai.configure(api_key="your-gemini-key")
adapter = StigmemGeminiAdapter.from_env()

tool_obj = genai.protos.Tool(
    function_declarations=[
        genai.protos.FunctionDeclaration(**t)
        for t in adapter.gemini_tools()
    ]
)

model = genai.GenerativeModel(
    model_name="gemini-2.0-flash",
    system_instruction="You are an agent with access to a shared knowledge base.",
    tools=[tool_obj],
)

chat = model.start_chat()
response = chat.send_message("What facts exist about project:acme-platform?")

# Dispatch tool calls
for part in response.candidates[0].content.parts:
    if part.function_call:
        result_json = adapter.dispatch(part.function_call.name, dict(part.function_call.args))
        # wrap result_json in a FunctionResponse and continue the loop
```

### Agentic loop (convenience)

```python
import google.generativeai as genai
from stigmem_plugin_gemini import StigmemGeminiAdapter

genai.configure(api_key="your-gemini-key")
adapter = StigmemGeminiAdapter.from_env()

answer = adapter.run(
    system_prompt="You are a helpful agent with access to a shared knowledge base.",
    user_message="Summarise all active roadmap constraints for project:acme.",
)
print(answer)
```

## Enable

The adapter has no node-global behavior gate at v0.1.0. Enable it in the host
application by installing the package and importing
`stigmem_plugin_gemini.StigmemGeminiAdapter`.

```bash
python -m pip install 'stigmem-plugin-gemini-adapter>=0.1.0,<2.0.0'
python -m pip install 'stigmem-plugin-gemini-adapter[gemini]>=0.1.0,<2.0.0'  # live Gemini loop
stigmem plugins list
```

## Disable

Remove the adapter from the host application path and restart the process that
loads plugins. If it was installed only for this integration, uninstall it:

```bash
python -m pip uninstall stigmem-plugin-gemini-adapter
```

## Test

```bash
cd stigmem
uv run pytest experimental/gemini-adapter/tests/ -v
```

No live node or Gemini API key required — all HTTP calls are mocked with respx.

## Uninstall

```bash
python -m pip uninstall stigmem-plugin-gemini-adapter
```

## Protocol notes

- Gemini uses `"OBJECT"`, `"STRING"`, `"NUMBER"`, `"BOOLEAN"`, `"INTEGER"`,
  `"ARRAY"` as type strings (upper-case). This differs from JSON Schema / OpenAI
  which use lower-case.
- The `run()` loop caps at `max_rounds=10` to prevent infinite tool-call cycles.
- Tool dispatch errors are caught and returned as `{"error": "..."}` JSON so the
  model can reason about failures rather than crashing the loop.
