PHPackages                             jvjvjv/code-talker - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. jvjvjv/code-talker

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

jvjvjv/code-talker
==================

Multi-provider AI communications package for Laravel — chatbots, streaming, tool-use, memory, and admin management.

v0.5.0(1mo ago)020↓85.7%MITPHPPHP ^8.2CI passing

Since Jun 5Pushed 5d agoCompare

[ Source](https://github.com/jvjvjv/CodeTalker)[ Packagist](https://packagist.org/packages/jvjvjv/code-talker)[ RSS](/packages/jvjvjv-code-talker/feed)WikiDiscussions develop Synced 1w ago

READMEChangelogDependencies (17)Versions (13)Used By (0)

code-talker
===========

[](#code-talker)

Multi-provider AI communications package for Laravel — chatbots, streaming, tool-use, memory, and admin management.

Requirements
------------

[](#requirements)

- PHP ^8.3
- Laravel ^12.62 || ^13.15

Provider communication runs on Laravel's first-party [laravel/ai](https://laravel.com/docs/13.x/ai-sdk)SDK, installed as a dependency. You do **not** need to publish or configure `config/ai.php` — provider credentials come from `AiSystem` database records and are bridged into laravel/ai providers at runtime. (Publish it only if your app uses laravel/ai on its own.)

Installation
------------

[](#installation)

```
composer require jvjvjv/code-talker
```

The package installs the Laravel Inertia adapter as a runtime dependency because its public and admin controllers render Inertia responses.

Publish the config and migrations, then run them:

```
php artisan vendor:publish --tag=code-talker-config
php artisan vendor:publish --tag=code-talker-migrations
php artisan migrate
```

If you want to customize the package route files directly in the host app, publish them too:

```
php artisan vendor:publish --tag=code-talker-routes
```

Upgrading
---------

[](#upgrading)

After upgrading the package, **re-publish or reconcile your published config**:

```
php artisan vendor:publish --tag=code-talker-config --force
```

This matters because the package merges its config **shallowly** (Laravel's `mergeConfigFrom`). If your app already has a published `config/code-talker.php`with a `providers` key, newer or corrected **nested** keys the package ships — most notably `providers.*.base_url` and the `raw_exchanges` block — are **not**backfilled into it. Your previously published array is used as-is, so a stale publish can silently keep an outdated provider base URL (see [Troubleshooting](#troubleshooting)).

After `--force`, re-apply any local customizations. If you'd rather not overwrite, diff your file against `vendor/jvjvjv/code-talker/config/code-talker.php`and copy over only the new keys.

Configuration
-------------

[](#configuration)

`config/code-talker.php` controls package-wide behavior:

KeyDefaultDescription`user_model``App\Models\User::class`Eloquent model used for authenticated users`middleware``['web']`Middleware applied to public chat routes`admin_middleware``['web', 'auth', 'can:manage-ai-tools']`Middleware applied to admin routes`reserved_slugs``[]`Additional slugs that cannot be used for root-path chatbots`schedule``true`Set to `false` to disable the package's automatic scheduled jobs### Suggested host-app packages

[](#suggested-host-app-packages)

- `bspdx/keystone` is suggested if you want a ready-made host-app authorization layer for the package's admin AI routes.

### Provider environment variables

[](#provider-environment-variables)

API keys, models, and token limits live on `AiSystem` database records — not in env vars. The env vars below only supply fallback base URLs (used when an `AiSystem` has no `base_url`), the Anthropic API version, and the LM Studio server URL:

```
ANTHROPIC_API_VERSION=2023-06-01
ANTHROPIC_BASE_URL=https://api.anthropic.com/v1
OPENAI_BASE_URL=https://api.openai.com/v1
GEMINI_BASE_URL=https://generativelanguage.googleapis.com/v1beta
GROK_BASE_URL=https://api.x.ai/v1
LMSTUDIO_SERVER_URL=http://localhost:1234

```

The `providers.*.pricing` config keys feed conversation usage/cost tracking.

**Base URLs must include the API version path segment** — `/v1` for `anthropic`, `openai`, and `grok`; `/v1beta` for `gemini` (the defaults above already do). Provider communication treats the configured URL as the complete base and appends the endpoint directly. This differs from the retired `anthropic-ai/sdk`, which accepted a bare host (`https://api.anthropic.com`) and appended `/v1` itself — so a bare host now produces 404s. This applies both to a live `AiSystem.base_url` value and to these `providers.*.base_url` fallbacks. See [Troubleshooting](#troubleshooting).

### Raw Provider Exchange Logging

[](#raw-provider-exchange-logging)

Every laravel/ai HTTP request/response can be captured verbatim into the `ai_provider_exchanges` table for debugging and auditing.

```
'raw_exchanges' => [
    'enabled' => env('CODE_TALKER_RAW_EXCHANGES_ENABLED', true),
    'providers' => env('CODE_TALKER_RAW_EXCHANGES_PROVIDERS', 'lm-studio'),
    'retention_days' => (int) env('CODE_TALKER_RAW_EXCHANGES_RETENTION_DAYS', 14),
],
```

- `enabled` — master switch for capture.
- `providers` — comma-separated allow-list of `AiSystem` provider values (`lm-studio`, `anthropic`, `openai`, `openai-compatible`, `gemini`, `grok`), or `all` to capture every provider. Defaults to `lm-studio`.
- `retention_days` — rows older than this are removed by `php artisan ai:prune-provider-exchanges`, scheduled daily at 03:00 (respects the `schedule` flag).

Request bodies and response bytes are stored, but request **headers are never recorded**, so provider API keys are not persisted.

### Troubleshooting

[](#troubleshooting)

**`Provider is unavailable: HTTP request returned status code 404`** — returned by the model-status / readiness check (and cloud-provider chat also 404s) for `anthropic`, `openai`, `gemini`, or `grok`.

- **Cause:** the provider base URL is missing its version segment — e.g. `https://api.anthropic.com` instead of `https://api.anthropic.com/v1`. This is usually a **stale published config** (the shallow merge described in [Upgrading](#upgrading) never backfilled the corrected default) or an old `AiSystem.base_url` / `ANTHROPIC_BASE_URL` value carried over from the `anthropic-ai/sdk` era.
- **Fix:** set the `AiSystem.base_url` to include `/v1` (or clear it to fall back to the config default), fix any bare-host `*_BASE_URL` env var, and re-publish the config with `--force` (see [Upgrading](#upgrading)). The correct URL forms are listed under [Provider environment variables](#provider-environment-variables).

AI Systems
----------

[](#ai-systems)

An `AiSystem` record represents a fully configured provider endpoint. Create one through the admin UI at `/admin/ai/systems` or via a seeder. Key fields:

FieldDescription`provider`One of: `anthropic`, `openai`, `openai_compatible`, `gemini`, `grok`, `lm-studio``model`Provider-specific model name`api_key`Stored encrypted`max_tokens`Maximum output tokens per request`temperature`Sampling temperature (overrides bot-level default)`context_length`Context window for local models (LM Studio)`enable_thinking`Enable extended thinking / reasoning output (Anthropic)`allowed_tools`Array of tool names the model may invoke`system_prompt_id`Optional FK to an `AiSystemPrompt` record`is_active`Inactive systems are rejected by the factory### Getting an agent in code

[](#getting-an-agent-in-code)

`AgentFactory` bridges an `AiSystem` record into a configured [laravel/ai](https://laravel.com/docs/13.x/ai-sdk) agent:

```
use Jvjvjv\CodeTalker\Services\LaravelAi\AgentFactory;
use Jvjvjv\CodeTalker\Models\AiSystem;

// From a specific system record
$agent = app(AgentFactory::class)->forSystem(
    AiSystem::find($id),
    instructions: 'You are a helpful assistant.',
    maxTokens: 2048,
    temperature: 0.7,
);

// From a feature key (resolves the default system for that feature)
$agent = app(AgentFactory::class)->forFeature('my-feature');

$response = $agent->prompt('Hello!');   // Laravel\Ai\Responses\AgentResponse
echo $response->text;

foreach ($agent->stream('Hello!') as $event) {
    // Laravel\Ai\Streaming\Events\* (TextDelta, ToolCall, StreamEnd, ...)
}
```

The agent runs on laravel/ai, so everything from the [laravel/ai documentation](https://laravel.com/docs/13.x/ai-sdk) — streaming, tool use, structured output — applies. Prior versions returned an `AiClientContract` from `AiClientFactory`; both were removed in 0.6.0.

### Feature defaults

[](#feature-defaults)

Map a feature key to a default `AiSystem` via the `ai_system_feature_defaults` table (managed through `/admin/ai/systems`). This decouples application code from specific system IDs.

Chat Bots
---------

[](#chat-bots)

An `AiChatBot` defines a user-facing persona. Create one at `/admin/ai/chat-bots`. Key fields:

FieldDescription`ai_system_id`The backing `AiSystem``name`Display name`slug`URL-safe identifier, must be unique`access_path``chat` → `/chat/{slug}`, `root` → `/{slug}``prompt_template`System prompt with optional placeholders (see below)`require_visitor_identity`Prompt anonymous visitors for name and email`tools_enabled`Whether the bot may invoke registered tools`temperature`Overrides `AiSystem` temperature for this botChatbot authentication and authorization are not managed by this package. The consuming application must decide which users or guests can reach chatbot routes by applying its own middleware, gates, or policies around the package routes.

### Prompt template placeholders

[](#prompt-template-placeholders)

These tokens are replaced when a conversation starts:

PlaceholderValue`{{bot_name}}`Bot's display name`{{bot_slug}}`Bot's slug`{{bot_description}}`Bot's description field`{{visitor_name}}`Name collected from anonymous visitor (if any)`{{visitor_email}}`Email collected from anonymous visitor (if any)The final system prompt is assembled as: `AiSystemPrompt.content` + prompt template + `## Learned Insights` (injected memories).

### Auto-registered routes

[](#auto-registered-routes)

All routes use the middleware from `code-talker.middleware`.

If `routes/codetalker-chatbots.php` or `routes/codetalker-admin.php` exists in the host app, the package will load those published copies instead of its internal defaults.

The package does not treat any bot as inherently public or private. If some chatbot routes should require authentication or further authorization, enforce that entirely in the consuming application by changing `code-talker.middleware`or wrapping the package routes in your own authorization layer.

RouteDescription`GET /chats`List of available bots (Inertia: `ai/ChatBotsIndex`)`GET /chats/statuses`JSON model-readiness status for all bots`GET /chat/{slug}`Chat UI for a bot (Inertia: `ai/ChatBot`)`GET /chat/{slug}/new`Start a new conversation`GET /chat/{slug}/status`JSON readiness status for one bot`POST /chat/{slug}/warmup`Warm up the model`POST /chat/{slug}/messages`Send a message (SSE stream)`POST /chat/{slug}/reset`Clear current conversation`POST /chat/{slug}/switch`Switch to a different conversation from history`GET /chat/{slug}/{hash}`Load a conversation by its shareable hashRoot-access-path bots duplicate the above at `/{slug}` instead of `/chat/{slug}`.

### Conversation state

[](#conversation-state)

The browser's active and historical conversations are stored in a session key and a 180-day encrypted cookie (`ai_chat_bot_conversations_{id}`). Conversations are also shareable via `/chat/{slug}/{hash}`.

Tool Registration
-----------------

[](#tool-registration)

Tools are [laravel/mcp](https://github.com/laravel/mcp) `Tool` classes. The same class runs in the local chat loop **and** can be exposed to external MCP clients (see [External MCP Server](#external-mcp-server)). Extend `Laravel\Mcp\Server\Tool`:

```
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Jvjvjv\CodeTalker\Support\ToolContext;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\ResponseFactory;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Attributes\Name;
use Laravel\Mcp\Server\Tool;

#[Name('get-weather')]
#[Description('Returns current weather for a given city.')]
class GetWeatherTool extends Tool
{
    public function __construct(
        private ToolContext $context,
    ) {}

    public function schema(JsonSchema $schema): array
    {
        return [
            'city' => $schema->string()->description('City name')->required(),
        ];
    }

    public function handle(Request $request): Response|ResponseFactory
    {
        // ... fetch weather ...
        return Response::structured(['temperature' => '72°F', 'condition' => 'Sunny']);
        // Use Response::error('...') to signal a failure to the model.
    }
}
```

Notes:

- The tool **name** defaults to the kebab-cased class basename (`GetWeatherTool` → `get-weather-tool`), so set an explicit `#[Name('get-weather')]` for a clean name.
- Inject `ToolContext` for the current user/conversation rather than depending on `AiConversation` directly. In the local chat loop it carries the live conversation (`$context->conversation`), derived `userId`, `visitorEmail`, and `feature`. When the tool is called by an external MCP client it carries the authenticated user's id with no conversation, so guard conversation-only logic.

Register the directory containing your tools in `AppServiceProvider`:

```
use Jvjvjv\CodeTalker\CodeTalkerServiceProvider;

public function register(): void
{
    CodeTalkerServiceProvider::addToolDirectory(
        app_path('Services/Mcp/Tools'),
        'App\\Services\\Mcp\\Tools\\'
    );
}
```

Tools are auto-discovered from registered directories. The `AiSystem::allowed_tools`array controls which discovered tools are exposed to the model for a given bot, by tool name.

> **Upgrading from a previous version:** the old `AiToolHandlerContract`(`name()`/`description()`/`schema(): array`/`handle(array): array`) is deprecated but still discovered and dispatched for backward compatibility. Migrate to `Laravel\Mcp\Server\Tool` as shown above. The built-in tools were also renamed from snake\_case to kebab-case — run the published migration to update any persisted `allowed_tools` values.

### Built-in tools

[](#built-in-tools)

The package includes built-in tools under `src/Services/Mcp/Tools/ChatBot`.

- `fetch-web-page`: Fetches readable text from a URL.
- `scan-memories`: Searches stored user memories for relevant context.
- `search-web`: Searches Bing, Google, DuckDuckGo, and Brave, then returns structured results plus markdown links/snippets.

To enable the web-search tool for a system, include `search-web` in `AiSystem::allowed_tools`.

`search-web` input schema (high level):

- `query` (required): Search query text.
- `engines` (optional): Any subset of `bing`, `google`, `duckduckgo`, `brave`.
- `page` (optional): Page number for continued searching.
- `per_engine_limit` (optional): Max results per engine (1-10).

`search-web` response includes:

- Per-engine results with `title`, `url`, and `description`.
- `markdown` containing clickable links and snippets.
- `next_page_input` to continue searching on the next page.
- Guidance for asking the model to inspect a specific link in depth.

### Injecting extra dependencies into tools

[](#injecting-extra-dependencies-into-tools)

If your tools need objects that aren't in the service container by default (e.g., a service scoped to the current conversation), register a parameter resolver:

```
CodeTalkerServiceProvider::registerToolParameterResolver(
    fn (AiConversation $conversation): array => [
        'myService' => app(MyService::class)->forConversation($conversation),
    ]
);
```

The resolver is called once per `ChatBotToolRegistry` instantiation, and its return values are passed as `makeWith()` overrides when tools are resolved from the container.

External MCP Server
-------------------

[](#external-mcp-server)

Because tools are laravel/mcp `Tool` classes, the same tools can be exposed to external MCP clients (Claude Desktop, Grok, etc.) through a bundled `CodeTalkerServer`. This is **disabled by default**. Enable it under the `code-talker.mcp` config key:

```
'mcp' => [
    'enabled' => env('CODE_TALKER_MCP_ENABLED', false),

    'web' => [
        'enabled' => true,
        'path' => env('CODE_TALKER_MCP_PATH', 'mcp/code-talker'),
        'middleware' => ['auth:sanctum'],
    ],

    'local' => [
        'enabled' => false,
        'handle' => env('CODE_TALKER_MCP_LOCAL_HANDLE', 'code-talker'),
    ],
],
```

- **web** registers an HTTP MCP endpoint via `Mcp::web()`. Protect it with authentication middleware (Sanctum or OAuth per the laravel/mcp docs). The authenticated user is mapped to a `ToolContext` so user-scoped tools such as `scan-memories` resolve the correct identity. Since there is no conversation in this context, `scan-memories` searches across all of the user's memories rather than a single feature.
- `scan-memories` implements `shouldRegister()` and is therefore only advertised to callers that have a user identity — anonymous callers never see it. If you expose the server on a public route, give it *optional* authentication middleware (authenticate when a token is present without rejecting guests) so authenticated callers still get the memory tool while anonymous callers get the stateless tools.
- **local** registers a stdio server via `Mcp::local()`, runnable through the `php artisan mcp:start {handle}` command for local AI assistant integrations.

The server requires laravel/mcp, which is installed as a dependency. See the [Laravel MCP documentation](https://laravel.com/docs/mcp) for client configuration, authentication, and the MCP Inspector.

Memory System
-------------

[](#memory-system)

After each completed conversation, `ProcessAiMemoryJob` dispatches and calls `AiMemoryService::processCompletedConversation()`. The service sends the conversation to the same `AiSystem` for analysis and extracts structured memory operations (add / update / remove).

Memories are stored in `AiFeatureMemory` and scoped per user:

- Authenticated users: scoped by `user_id`
- Anonymous visitors: scoped by `visitor_email` (requires `require_visitor_identity = true`)

### Memory categories

[](#memory-categories)

CategoryDescription`preference`How the user likes things done`domain_knowledge`Facts about the user not covered by other data`system_tuning`What worked well or poorly in this bot's approachMemories are ranked by `confidence` and `times_reinforced` and injected into the system prompt under `## Learned Insights`. Memories can be reviewed and edited at `/admin/ai/memories`.

To rebuild all memories for a feature from historical conversations:

```
# Via admin UI at /admin/ai/memories — use the "Rebuild" action
# Or via code:
app(\Jvjvjv\CodeTalker\Services\AiMemoryService::class)->rebuildMemories('chat-bot:my-bot');
```

Admin Routes
------------

[](#admin-routes)

The admin route group is registered under `/admin/ai/*` and uses the middleware defined in `code-talker.admin_middleware`, which defaults to `['web', 'auth', 'can:manage-ai-tools']`.

If your host app does not already provide that gate, wire it yourself or change `code-talker.admin_middleware` to the authorization middleware your application already uses.

All admin routes are under `/admin/ai` and require the `can:manage-ai-tools` gate (configurable via `admin_middleware`).

PrefixResource`/admin/ai/systems`AI Systems CRUD + interaction log viewer`/admin/ai/system-prompts`Reusable system prompt CRUD`/admin/ai/chat-bots`Chat bot CRUD`/admin/ai/conversations`Conversation viewer + usage backfill trigger`/admin/ai/memories`Feature memory CRUD + per-feature rebuildDefine the `manage-ai-tools` gate in your `AppServiceProvider` or `AuthServiceProvider`:

```
Gate::define('manage-ai-tools', fn ($user) => (bool) $user->is_admin);
```

Scheduled Jobs
--------------

[](#scheduled-jobs)

The package registers two jobs automatically (requires Laravel's scheduler to be running):

JobScheduleDescription`ai:sync-conversation-usage`Twice daily (00:00, 12:00)Syncs token counts and cost to `AiConversation``BackfillConversationUsageJob`Daily at 02:30Backfills usage for conversations missing cost dataDisable automatic scheduling in config and register manually if needed:

```
// config/code-talker.php
'schedule' => false,

// Your app's Console\Kernel or routes/console.php
Schedule::command('ai:sync-conversation-usage')->twiceDaily();
```

Artisan Commands
----------------

[](#artisan-commands)

```
# Detect and store capability flags for all AiSystem records
php artisan ai:backfill-system-capabilities

# Backfill usage/cost data for conversations missing it
php artisan ai:backfill-conversation-usage

# Sync current token/cost totals to ai_conversations
php artisan ai:sync-conversation-usage
```

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance96

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity43

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

11

Last Release

35d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/e18e4a98724a638f28cedf7e66eb88bf4f6cc176edc128caaf996d8048a62d9d?d=identicon)[jvjvjv](/maintainers/jvjvjv)

---

Top Contributors

[![jvjvjv](https://avatars.githubusercontent.com/u/1606080?v=4)](https://github.com/jvjvjv "jvjvjv (23 commits)")

---

Tags

laravelaistreamingopenaiGeminichatbotllmanthropicgrok

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/jvjvjv-code-talker/health.svg)

```
[![Health](https://phpackages.com/badges/jvjvjv-code-talker/health.svg)](https://phpackages.com/packages/jvjvjv-code-talker)
```

###  Alternatives

[statamic/cms

The Statamic CMS Core Package

4.8k3.6M1.0k](/packages/statamic-cms)[mozex/anthropic-laravel

Laravel integration for the Anthropic API: facade, config publishing, install command, testing fakes, messages, streaming, tool use, thinking, and batches.

74331.3k1](/packages/mozex-anthropic-laravel)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
