# @mohamedtebo/convai-platform — API Integration Guide

Integration of this SDK into the **api** service for OpenAI **Responses + Conversations**, with MongoDB persistence and custom function tools.

> **Package:** `@mohamedtebo/convai-platform`  
> **Consumer adapters:** `api/src/core/convai-adapters/` (in the api monorepo)

---

## Table of contents

1. [Architecture](#architecture)
2. [Environment variables](#environment-variables)
3. [Setup examples](#setup-examples)
4. [Production rollout & migration scripts](#production-rollout--migration-scripts)
5. [User message → AI reply lifecycle](#user-message--ai-reply-lifecycle)
6. [WhatsApp integration cycle](#whatsapp-integration-cycle)
7. [Facebook Messenger integration cycle](#facebook-messenger-integration-cycle)
8. [Tool loop lifecycle](#tool-loop-lifecycle)
9. [Vision (images)](#vision-images)
10. [Troubleshooting](#troubleshooting)
11. [Publish this package](#publish-this-package)

---

## Architecture

```
Widget / Channel
       │
       ▼
api (Socket: ask-question)
       │
       ▼
ThreadRunQueue ──► getBotMessageForResponses()
       │
       ├── AI_USE_CONVAI_SDK=false  → legacy response-tool-loop (deprecated)
       │
       └── AI_USE_CONVAI_SDK=true   → getBotMessageForResponsesViaSdk()
                │
                ├── buildResponseConfig()        ← instructions, tools, model
                ├── enrichResponsesUserQuestion() ← vision when enabled
                ├── getAIPlatformClient()        ← cached SDK client
                └── sdkClient.safe.askWithTools()
                         │
                         ├── MongoConversationStore
                         ├── ApiToolHandler       ← tickets, calendar, custom functions
                         └── OpenAI Responses API tool loop (this SDK)
                │
                ▼
AssistantResponseService / handleFinalResponseForResponses
       │
       ▼
Socket BOT_REPLIED → widget
```

### api adapter files

| File | Role |
|------|------|
| `api/src/core/convai-adapters/ai-platform.factory.ts` | Cached `PlatformClient` |
| `api/src/core/convai-adapters/mongo-conversation.store.ts` | `Thread.openai_conversation_id` / `last_response_id` |
| `api/src/core/convai-adapters/telbany-tool.handler.ts` | **ApiToolHandler** → `run-completion.service` |
| `api/src/core/convai-adapters/tool-loop-context.loader.ts` | Chatbot functions + org context |
| `api/src/core/services/ai/providers/openai/response-config.builder.ts` | Instructions, tools, model |
| `api/src/api/v1/routes/openai/helpers.ts` | `getBotMessageForResponses()` |

### SDK files (this package)

| File | Role |
|------|------|
| `src/composed/platform-client.ts` | `createPlatformClient()` |
| `src/providers/openai/openai-responses.provider.ts` | Responses provider |
| `src/providers/openai/loops/responses-tool.loop.ts` | Tool loop (max 10 turns) |
| `src/providers/openai/operations/create-response.operation.ts` | `responses.create` |
| `src/providers/openai/operations/submit-tool-outputs.operation.ts` | Tool output submit |
| `src/types/conversation-store.interface.ts` | `ConversationStore` contract |
| `src/types/tool-handler.interface.ts` | `ToolHandler` contract |

---

## Environment variables

In **api** `.env`:

```env
AI_BACKEND_DEFAULT=assistants
AI_RESPONSES_MODEL_DEFAULT=gpt-4o-mini
AI_USE_CONVAI_SDK=false
OPENAI_API_KEY=sk-...
```

| Variable | Values | When to change |
|----------|--------|----------------|
| `AI_BACKEND_DEFAULT` | `assistants` \| `responses` | After migration steps 01–04 |
| `AI_USE_CONVAI_SDK` | `true` \| `false` | After parity test (step 06) on staging |
| `AI_RESPONSES_MODEL_DEFAULT` | e.g. `gpt-4o-mini` | Optional default model |

MongoDB per chatbot:

| Field | Purpose |
|-------|---------|
| `chatbots.ai_backend` | `assistants` or `responses` |
| `generalsettings.openai_vision_enabled` | Image analysis before model call |

---

## Setup examples

### Install

```bash
npm install @mohamedtebo/convai-platform openai
```

### Standalone SDK

```typescript
import { createPlatformClient, MemoryConversationStore } from '@mohamedtebo/convai-platform';

const client = createPlatformClient({
  provider: 'openai-responses',
  apiKey: process.env.OPENAI_API_KEY!,
  model: 'gpt-4o-mini',
  conversationStore: new MemoryConversationStore(),
});

const result = await client.safe.askWithTools({
  threadKey: 'resp_conv_abc123',
  chatbotId: '507f1f77bcf86cd799439011',
  organizationId: 'org_001',
  userQuestion: 'I want to book a ticket for tomorrow',
  instructions: 'You are a helpful assistant.',
  tools: [],
});

if (result.success && result.data.kind === 'text') {
  console.log(result.data.outputText);
}
```

### With api adapters (production)

```typescript
import { getAIPlatformClient } from './core/convai-adapters';

const client = getAIPlatformClient({ backend: 'openai-responses', model: 'gpt-4o-mini' });

const result = await client.safe.askWithTools({
  threadKey: threadId,
  chatbotId,
  organizationId,
  userQuestion: enrichedQuestion,
  instructions: params.instructions,
  tools: params.tools,
  vectorStoreIds: vectorStoreId ? [vectorStoreId] : undefined,
  model: params.model,
  mediaUrl,
});
```

### Custom ConversationStore + ToolHandler

```typescript
import { createPlatformClient } from '@mohamedtebo/convai-platform';
import type { ConversationStore, ToolHandler } from '@mohamedtebo/convai-platform';

const client = createPlatformClient({
  provider: 'openai-responses',
  apiKey: process.env.OPENAI_API_KEY!,
  model: 'gpt-4o',
  conversationStore: myStore as ConversationStore,
  toolHandler: myHandler as ToolHandler,
});
```

---

## Production rollout & migration scripts

Run from the **api** service root (`MONGO_URI`, `OPENAI_API_KEY` required).

### Steps (must run before production Responses + SDK)

| Step | Purpose | Changes DB? |
|------|---------|-------------|
| **01** | Audit chatbots, assistant IDs, vector stores | No |
| **02** | Verify `General.system_prompt` for all chatbots | Conditionally |
| **03** | Backfill `openai_conversation_id` from Assistants threads | Yes |
| **04** | Verify threads missing conversation ID | No |
| **05** | Set `ai_backend=responses` on pilot chatbots | Yes |
| **06** | Golden-path parity Q&A test | API calls only |

### Commands

```bash
cd api

# Audit
AI_MIGRATION_DRY_RUN=true MIGRATION_STEPS=01,02 \
  ts-node -r tsconfig-paths/register migrations/openai-responses/run-openai-responses-migration.ts

# Backfill (active threads, 90 days)
AI_MIGRATION_DRY_RUN=false MIGRATION_STEPS=03 \
  ts-node -r tsconfig-paths/register migrations/openai-responses/run-openai-responses-migration.ts

# Verify
AI_MIGRATION_DRY_RUN=false MIGRATION_STEPS=04 \
  ts-node -r tsconfig-paths/register migrations/openai-responses/run-openai-responses-migration.ts

# Enable Responses on pilot chatbots
AI_MIGRATION_DRY_RUN=false MIGRATION_STEPS=05 \
  MIGRATION_CHATBOT_IDS=CHATBOT_ID_1,CHATBOT_ID_2 \
  ts-node -r tsconfig-paths/register migrations/openai-responses/run-openai-responses-migration.ts

# Parity test
AI_MIGRATION_DRY_RUN=false MIGRATION_STEPS=06 \
  PARITY_CHATBOT_ID=CHATBOT_ID_1 \
  ts-node -r tsconfig-paths/register migrations/openai-responses/run-openai-responses-migration.ts

# Deploy api: AI_USE_CONVAI_SDK=true
```

### Production checklist

| Order | Action |
|-------|--------|
| 1 | Deploy api code (SDK adapters + fixes) with `AI_USE_CONVAI_SDK=false` |
| 2 | Run migration steps **01–04** on production Mongo |
| 3 | Run step **05** on 2–3 internal chatbots |
| 4 | Run step **06** parity on staging, then prod sample |
| 5 | Set `AI_USE_CONVAI_SDK=true` on staging → soak → production |
| 6 | Expand step **05** (5% → 25% → 100% chatbots) |

### Rollback

```bash
AI_MIGRATION_DRY_RUN=false ROLLBACK_CHATBOT_IDS=CHATBOT_ID \
  ts-node -r tsconfig-paths/register migrations/openai-responses/repair/reset-ai-backend.step.ts
```

Set `AI_USE_CONVAI_SDK=false` and redeploy.

---

## User message → AI reply lifecycle

When `ai_backend=responses` and `AI_USE_CONVAI_SDK=true`:

| # | Step | Location | What happens |
|---|------|----------|--------------|
| 1 | User sends message | Widget → Socket `ask-question` | `userQuestion`, optional `mediaUrl` |
| 2 | Save message | `api/.../bot-thread-active.handler.ts` | `userQuestionMessage()` → Mongo `Message` |
| 3 | Enqueue job | `thread-run-queue.service.ts` | One run per `threadId` (batch or sequential) |
| 4 | Resolve backend | `resolve-job-backend.helper.ts` | `resp_*` or `ai_backend=responses` |
| 5 | Batch path | `batch-run-processor.ts` | `getBotMessageForResponses()` (not Assistants API) |
| 6 | Vision | `helpers.ts` → `enrichResponsesUserQuestion()` | `buildVisionMessageContent()` if enabled |
| 7 | Config | `response-config.builder.ts` | `system_prompt`, context, function tools |
| 8 | SDK | `getBotMessageForResponsesViaSdk()` | `askWithTools()` |
| 9 | Load state | `mongo-conversation.store.ts` | `Thread.openai_conversation_id` |
| 10 | OpenAI | SDK `create-response.operation.ts` | `responses.create` |
| 11 | Tools? | SDK `responses-tool.loop.ts` | See tool loop section |
| 12 | Post-process | `handleFinalResponseForResponses()` | Tickets, markers, widget shape |
| 13 | Reply | Socket `onResult` | `BOT_REPLIED` event |

**Thread IDs**

| Backend | Format | Created in |
|---------|--------|------------|
| Assistants | `thread_…` | OpenAI Threads API |
| Responses | `resp_conv_…` | OpenAI Conversations API |

> **Full guide (Widget + WhatsApp + Facebook + migration):**  
> api monorepo: `api/docs/COMPLETE_CONVAI_SDK_INTEGRATION_GUIDE.md`

---

## WhatsApp integration cycle

```
WhatsApp user → Meta webhook → handle-event.ts → handle-incoming-message.ts
  → BullMQ queue_worker (addTextJob / addMediaJob)
  → handleChannelAskQuestion('whatsapp')
  → userQuestionMessage() + ThreadRunQueue.enqueueJob()
  → getBotMessageForResponses() [SDK when AI_USE_CONVAI_SDK=true]
  → dispatchBotReply() → WhatsAppAdapter.sendMessage() → user
```

| # | Step | File |
|---|------|------|
| 1 | Meta webhook POST | `whatsapp/webhook/handle-event.ts` |
| 2 | Parse text/media/button | `handle-incoming-message.ts` |
| 3 | Redis queue | `queue_worker.ts` |
| 4 | Channel routing | `channel-ask-question.handler.ts` |
| 5 | Save + enqueue AI | `existing-lead-bot-text.handler.ts` |
| 6 | AI pipeline | Shared (see above) |
| 7 | Outbound | `channel-bot-reply.handler.ts` → `WhatsAppAdapter` |

WhatsApp-specific: typing indicator, interactive calendar lists, media upload to Wasabi.

---

## Facebook Messenger integration cycle

```
Facebook user → Meta webhook → FacebookAdapter.handleEvent()
  → fetchMessengerMedia() (attachments) → BullMQ queue_worker
  → handleChannelAskQuestion('facebook')
  → userQuestionMessage() + ThreadRunQueue.enqueueJob()
  → getBotMessageForResponses() [SDK when AI_USE_CONVAI_SDK=true]
  → dispatchBotReply() → FacebookAdapter.sendMessage() → user
```

| # | Step | File |
|---|------|------|
| 1 | Meta webhook POST | `FacebookAdapter.ts` |
| 2 | Skip echo, resolve page | `handleEvent()` |
| 3 | Media / audio transcript | `fetchMessengerMedia()`, `transcribeAudioToText()` |
| 4 | Redis queue | `queue_worker.ts` |
| 5 | Channel routing | `channel-ask-question.handler.ts` |
| 6 | Save + enqueue AI | `existing-lead-bot-text.handler.ts` |
| 7 | Outbound | `channel-bot-reply.handler.ts` → `FacebookAdapter` |

---

## Tool loop lifecycle

Triggered when `response.output[]` contains `type: function_call`.

| # | Step | Location | Description |
|---|------|----------|-------------|
| 1 | Detect calls | SDK `responses-tool.loop.ts` | Scan output for `function_call` |
| 2 | Build output | api `run-completion.service.ts` | Route by function name |
| 3 | Missing params | api `query-function.context.ts` | JSON + `MANDATORY_RESPONSE_FORMAT` |
| 4 | Submit | SDK `submit-tool-outputs.operation.ts` | Chain via `conversation` or `previous_response_id` |
| 5 | Cap | `MAX_TURNS = 10` | Prevent infinite loops |
| 6 | Extract | api `ApiToolHandler.extractResult()` | Ticket / API when complete |
| 7 | Short-circuit | SDK `kind: 'toolResult'` | `AssistantResponseService.run()` |
| 8 | Continue | `extractResult` returns `null` | Model asks user for missing fields |

**Tool sources**

| Source | Examples |
|--------|----------|
| Platform tools | `notifyAgent`, calendar, `get_current_date` |
| Custom (Mongo) | `create_ticket_for_booking`, `job_application` |
| File search | When chatbot has vector store |

---

## Vision (images)

When `openai_vision_enabled=true`:

1. api `enrichResponsesUserQuestion()` → `buildVisionMessageContent()`
2. `VisionFactory.analyzeImages()` → `Image analysis: …` in text
3. Enriched text sent as `userQuestion` to SDK

---

## Troubleshooting

| Symptom | Cause | Fix area |
|---------|-------|----------|
| `Invalid thread_id resp_conv_…` | Assistants API on Responses thread | api batch processor |
| `Invalid schema … image` | Raw param type to OpenAI | api `function-param-schema.helper.ts` |
| Ignores images | Vision not enriched | api `enrichResponsesUserQuestion` |
| Empty reply | SDK error | Check `[getBotMessageForResponses] SDK error` logs |
| Stale SDK in Docker | Old dist | Rebuild package + restart api |

---

## Publish this package

```bash
cd convai-platform
npm run build
npm publish --access public
```

If publish returns **404 / no permission**, run `npm login` with the account that owns the `@mohamedtebo` scope on npmjs.com. Until then, mount `./convai-platform/dist` in Docker.

---

MIT © mohamedtebo
