Metadata-Version: 2.4
Name: fluidtalk
Version: 1.0.1
Summary: Official Python client for the FluidTalk Handshake API — drop FluidTalk AI persona replies into your own chatbot backend.
Author: FluidTalk
License: MIT
Project-URL: Homepage, https://talk.fluidvip.com
Keywords: fluidtalk,chatbot,ai,handshake,sdk
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Intended Audience :: Developers
Classifier: Topic :: Communications :: Chat
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.25
Dynamic: license-file

# fluidtalk (Python)

Official Python client for the **FluidTalk Handshake API**. Drop FluidTalk's AI persona replies into your own chatbot backend without rebuilding the HTTP/auth/multipart layer.

You keep your own orchestration (when to open a chat, when to follow up, where to send messages); this client just gives you the persona's reply in one call.

```bash
pip install fluidtalk
```

```python
from fluidtalk import FluidTalk, FluidTalkError

ft = FluidTalk(
    api_key="ft_sk_...",            # scoped to one persona + engine
    own_username="@my_account",     # optional default for tracking / dedup
    # base_url="https://api-talk.fluidvip.com"  (default)
)

# A lead messaged you -> get the persona's reply and relay it on your platform
res = ft.inbound_chat(target="@john_doe", message="hey, saw your post!")
for bubble in res.replies:
    my_chat.send("@john_doe", bubble)
if res.should_send_photo and res.photo_url:
    my_chat.send_image("@john_doe", res.photo_url)
```

## Concepts → methods

A conversation can start four ways. The builder names map to these methods (the API wire value is handled for you):

| Builder name | When | Method |
| :-- | :-- | :-- |
| **Inbound Chat** | the lead messages first (or an ongoing chat) | `ft.inbound_chat(target, message)` |
| **AI Opener** | your persona messages first | `ft.ai_opener(target)` |
| **Event Trigger** | a platform event fires | `ft.event_trigger(target, event_type, context)` |
| **Follow-up** | re-engage a quiet lead | `ft.follow_up(target)` |
| — | host a local image for context | `ft.upload_image(path=...)` |

A conversation is keyed by **(persona, `target`)** — reuse the same `target` for the same lead and the engine keeps phase/state across turns.

## Examples

```python
# AI opener (optionally with a profile screenshot for better targeting)
opener = ft.ai_opener(target="@lead", image_path="./profile.jpg")
my_chat.send("@lead", opener.message)

# Platform event (event_type must match an Event Trigger configured in your engine)
ev = ft.event_trigger(target="@lead", event_type="story_reaction", context={"reaction": "🔥"})

# Re-engage a ghost (needs an existing conversation)
fu = ft.follow_up(target="@lead")

# Attach an image the lead sent
url = ft.upload_image(path="./incoming.jpg").url
r = ft.inbound_chat(target="@lead", message="what do you think?", image_url=url)
```

Results: `inbound_chat` → `ChatResult(reply, replies, should_send_photo, photo_url, session_id, ignored, ignore_reason)`; `ai_opener`/`event_trigger`/`follow_up` → `ActionResult(session_id, message, ignored, ignore_reason)`; `upload_image` → `UploadResult(url)`. Send `replies` as separate bubbles; when `should_send_photo`, also send `photo_url`.

## Behaviour to handle

```python
try:
    res = ft.inbound_chat(target="@lead", message="hi")
    if res.ignored:
        return  # duplicate lead, already owned by another account — send nothing
    for bubble in res.replies:
        my_chat.send("@lead", bubble)
except FluidTalkError as e:
    if e.status == 402:   # out of tokens (insufficient_tokens)
        ...
    elif e.status == 400: # conversation already DONE / sealed -> stop messaging
        ...
    elif e.status == 404: # no conversation yet (follow_up before any contact)
        ...
```

- **One token per conversation** — starting a new conversation costs one token; out of tokens → `FluidTalkError(status=402)`.
- **Sealed conversation** — when finished, the next `inbound_chat` raises `FluidTalkError(status=400)` ("This session is DONE").

API keys are **action-only**: send messages and upload images, never change personas/engines/billing (that stays in the dashboard). Keep your `ft_sk_` key secret.
