Metadata-Version: 2.5
Name: signalcore
Version: 0.1.0
Summary: The Python client for SignalCore: create agents, talk to them and run everything around them.
Project-URL: Homepage, https://signalcore.ai
Project-URL: Documentation, https://signalcore.ai/docs
Project-URL: Repository, https://github.com/OmnistGroup/signalcore/tree/staging/clients/python
License-Expression: MIT
License-File: LICENSE
Keywords: agents,ai,api,sdk,signalcore
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: anyio>=4.5
Requires-Dist: httpx>=0.28
Requires-Dist: pydantic>=2.7
Description-Content-Type: text/markdown

# signalcore

The Python client for [SignalCore](https://signalcore.ai): create agents, talk to them, and run
everything around them (conversations, approvals, knowledge, records, apps) from your own code.

```bash
pip install signalcore
```

Python 3.11 or newer. Create an API key in the web app and export it:

```bash
export SIGNALCORE_API_KEY=sk_live_...
```

## Quickstart

```python
import asyncio

from signalcore import AsyncSignalcore


async def main() -> None:
    async with AsyncSignalcore() as client:  # reads SIGNALCORE_API_KEY
        agent = await client.agents.create("Reception", instructions="You answer for Clínica Sol.")
        await client.agents.publish(agent.id)

        answer = await client.agents.invoke(
            agent.id, input="Do you open on Saturdays?", thread="ticket-7"
        )
        print(answer.text)

        async for event in client.runs.stream(answer.run_id):  # the run's journal, live
            print(event.event, event.json())


asyncio.run(main())
```

Code that does not use `asyncio` imports `Signalcore` instead: the same client, same names,
no `await`.

```python
from signalcore import Signalcore

with Signalcore() as client:
    for agent in client.agents.list().items:
        print(agent.name)
```

## What you get

- **Every endpoint of the API**, one attribute per family: `client.agents`, `client.runs`,
  `client.conversations`, `client.requests`, `client.documents`, `client.records`,
  `client.vault`, `client.connections`, `client.channels`, `client.skills` and the rest. Each
  method names the operation it calls.
- **Typed answers.** Responses are [pydantic](https://docs.pydantic.dev) models generated from
  the API's contract (`signalcore.types`), so your editor and type checker know every field.
- **Errors you can catch.** Failures raise `NotFoundError`, `ForbiddenError`, `InvalidError`
  and the like (all `SignalcoreError`), carrying the API's `status`, `code`, `detail` and, for
  a body that did not fit, the `errors` field by field.
- **Lists that page.** Every list answers `items` and `next_cursor`. To read a whole list:
  `client.iterate(client.conversations.list, status="open")`.
- **Streams.** `client.runs.stream(run_id)` yields Server-Sent Events until the run is `done`
  or `paused`; pass `last_event_id` to resume after a drop. `client.build.sessions.say(...)`
  streams a builder turn.
- **Several businesses.** An agency's key acts as each of its clients:
  `client.for_org("org_...").agents.list()`.
- **Live streams.** What a run shows as it happens is watched from a browser, over a WebSocket.
  `client.live.url(stream_id)` is its address (`wss://<api>/v1/live/<id>?token=...`, with this
  client's credential in the query): give it to `@signalcore/ui`'s `LiveView` or the TypeScript
  client's `live.connect`, only when the client holds an end user's token or a publishable key. It
  carries the credential, so never hand a browser one made with a secret key.

## End users in the browser

The people who use your product can talk to your agents without your secret key ever reaching
their browser. Your backend asks for a token for a person it knows and hands it over:

```python
minted = await client.end_users.mint_token("user-42", name="Ana", email="ana@example.com")
# Hand minted.token to their browser: new Signalcore({ endUserToken }) in @signalcore/sdk,
# or AsyncSignalcore(end_user_token=...) from Python.
```

For visitors who are not signed in, make a publishable key that works only from your pages
(`client.publishable_keys.create("Shop", ["https://shop.example.com"])`) and give the client
`publishable_key=` with a `visitor_id=` the browser keeps. Both credentials can talk to live
agents and read their own runs, and nothing else; `caller` is who the credential says.

## Your own components

What an agent can draw (cards, tables, forms) is a closed catalog, but your product has cards of
its own. Register one by name, with the props it takes and a text for doors that cannot draw it,
and agents can show it; your app draws it:

```python
await client.ui.components.put(
    "PropertyCard",
    "Property card",
    "A listing the customer asked about",  # what the agent should use it for
    {
        "type": "object",
        "properties": {"address": {"type": "string"}, "price": {"type": "number"}},
        "required": ["address", "price"],
    },
    "{address}\nPrice: {price} EUR",  # the text twin; {prop} is filled from the props
)
```

The schema is a small subset of JSON Schema: strings, numbers, whole numbers, booleans, lists of
those and one level of object; anything else is refused. An agent shows the component as a
`Host` node, `{"id": "home", "component": "Host", "name": "PropertyCard", "props": {...}}`
(`sc.ui.host("PropertyCard", {...})` in agent code). Its props are checked against your schema
before anyone sees them, and the text twin is the surface's text when the agent wrote none.
`client.ui.catalog()` lists your components after the built-in ones.

## Configuration

```python
AsyncSignalcore(
    api_key=None,  # else SIGNALCORE_API_KEY
    end_user_token=None,  # instead of api_key: a person of your product, in their browser
    publishable_key=None,  # instead of api_key: a visitor, with visitor_id (8-128 characters)
    visitor_id=None,
    base_url=None,  # else SIGNALCORE_BASE_URL, else https://signalcore.ai
    org=None,  # act as a business this key manages
    timeout=30,  # seconds
)
```

Pass exactly one of `api_key`, `end_user_token` and `publishable_key`.

Keys carry scopes (`agents:read`, `runs:write`...), so a key made for one job can do only
that job; a call outside its scopes raises `ForbiddenError`.

## Documentation

Guides and the full API reference: <https://signalcore.ai/docs>.

MIT licensed.
