定制 helgesverre/synapse 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

helgesverre/synapse

Composer 安装命令:

composer require helgesverre/synapse

包简介

A modern PHP library for LLM orchestration with executors, prompts, parsers, and tool calling

README 文档

README

A modern PHP 8.2+ library for LLM orchestration with executors, prompts, parsers, streaming, and tool calling. Inspired by llm-exe.

Features

  • Executor Pattern: Composable execution pipeline with lifecycle hooks
  • Prompt System: Template-based prompts with helpers, partials, and history
  • Parser System: Extract structured data from LLM responses (JSON, boolean, lists, enums, code blocks)
  • Tool/Function Calling: Built-in support for multi-step tool calling
  • State Management: Conversation history and context tracking
  • Streaming: Token streaming and streaming tool calls
  • Multi-Provider: OpenAI, Anthropic, Google/Gemini, Mistral, xAI, Groq, Moonshot, plus local models via Ollama
  • Embeddings: Unified embedding providers (OpenAI, Mistral, Jina, Cohere, Voyage, Ollama)
  • Runtime Modules: Trace bridge/exporters, checkpoints, memory store, workflow engine, and evaluation suite
  • Event Hooks: Lifecycle events for logging, metrics, and debugging
  • PSR Standards: PSR-4, PSR-7, PSR-17, PSR-18 compatible

Installation

composer require helgesverre/synapse

You'll also need an HTTP client (PSR-18) and HTTP factories (PSR-17). Synapse can auto-discover Guzzle or Symfony HTTP client if installed:

composer require guzzlehttp/guzzle

If you prefer Symfony:

composer require symfony/http-client

Quick Start

<?php

use function HelgeSverre\Synapse\{createChatPrompt, createExecutor, createParser, useLlm};

// Create provider, prompt, and parser (transport auto-discovered if available)
$llm = useLlm('openai', [
    'apiKey' => getenv('OPENAI_API_KEY'),
    'model' => 'gpt-4o-mini',
]);

$prompt = createChatPrompt()
    ->addSystemMessage('You are a helpful assistant.')
    ->addUserMessage('{{question}}', parseTemplate: true);

$parser = createParser('string');

// Create and execute
$executor = createExecutor([
    'llm' => $llm,
    'prompt' => $prompt,
    'parser' => $parser,
]);

$result = $executor->run(['question' => 'What is the capital of France?']);
echo $result->getValue(); // "Paris"

If you want to configure transport manually:

<?php

use HelgeSverre\Synapse\Factory;

$client = new \GuzzleHttp\Client();
$psr17Factory = new \GuzzleHttp\Psr7\HttpFactory();
Factory::setDefaultTransport(
    Factory::createTransport($client, $psr17Factory, $psr17Factory)
);

Core Concepts

Executors

Executors are the core building blocks that orchestrate the LLM pipeline.

use function HelgeSverre\Synapse\{createCoreExecutor, createExecutor};

// CoreExecutor - wrap any function
$calc = createCoreExecutor(fn($input) => $input['a'] + $input['b']);
$result = $calc->run(['a' => 5, 'b' => 3]);

// LlmExecutor - full LLM pipeline
$executor = createExecutor([
    'llm' => $provider,
    'prompt' => $prompt,
    'parser' => $parser,
    'model' => 'gpt-4o-mini',
]);

Prompts

Prompts use {{variable}} syntax for template replacement. Note: addUserMessage() defaults to parseTemplate: false, so pass parseTemplate: true when you want template rendering.

use function HelgeSverre\Synapse\{createChatPrompt, createTextPrompt};

// Chat prompt (recommended)
$prompt = createChatPrompt()
    ->addSystemMessage('You are an expert on {{topic}}.')
    ->addUserMessage('{{question}}', parseTemplate: true);

// Text prompt (simple)
$prompt = createTextPrompt()
    ->addContent('Answer this question about {{topic}}: {{question}}');

// Render with values
$messages = $prompt->render([
    'topic' => 'history',
    'question' => 'Who was Napoleon?',
]);

Template Features

// Nested paths
$prompt->addUserMessage('Hello {{user.name}}!', parseTemplate: true);

// Custom helpers
$prompt->registerHelper('upper', fn($s) => strtoupper($s));
$prompt->addUserMessage('{{upper name}}', parseTemplate: true); // Uses helper

// Partials (reusable snippets)
$prompt->registerPartial('greeting', 'Hello, {{name}}!');
$prompt->addUserMessage('{{> greeting}}', parseTemplate: true);

// Strict mode (throws on missing variables)
$prompt->strict(true);

Parsers

Extract structured data from LLM responses.

use function HelgeSverre\Synapse\createParser;

// String (default)
$parser = createParser('string');

// JSON with schema
$parser = createParser('json', [
    'schema' => [
        'type' => 'object',
        'properties' => [
            'name' => ['type' => 'string'],
            'age' => ['type' => 'number'],
        ],
    ],
]);

// Boolean (yes/no detection)
$parser = createParser('boolean');

// Number
$parser = createParser('number');

// List/Array
$parser = createParser('list');

// Key-value list
$parser = createParser('keyvalue', ['separator' => ':']);

// List to JSON
$parser = createParser('listjson', ['separator' => ':']);

// Code block extraction
$parser = createParser('code', ['language' => 'php']);

// Enum (match from allowed values)
$parser = createParser('enum', [
    'values' => ['low', 'medium', 'high'],
]);

// Custom
$parser = createParser('custom', [
    'handler' => fn($response) => customParse($response->getText()),
]);

Tool/Function Calling

use function HelgeSverre\Synapse\{createExecutor, createToolRegistry};

$tools = createToolRegistry([
    [
        'name' => 'get_weather',
        'description' => 'Get weather for a location',
        'parameters' => [
            'type' => 'object',
            'properties' => [
                'location' => ['type' => 'string'],
            ],
            'required' => ['location'],
        ],
        'handler' => fn($args) => ['temp' => 22, 'location' => $args['location']],
    ],
]);

$executor = createExecutor([
    'llm' => $provider,
    'prompt' => $prompt,
    'parser' => createParser('string'),
    'model' => 'gpt-4o-mini',
    'tools' => $tools,
    'maxIterations' => 10,
]);

Use createToolRegistry() to register tools for function calling.

Streaming

Streaming requires a stream-capable transport (for example GuzzleStreamTransport).

use GuzzleHttp\Client;
use HelgeSverre\Synapse\Executor\StreamingLlmExecutor;
use HelgeSverre\Synapse\Prompt\TextPrompt;
use HelgeSverre\Synapse\Provider\Http\GuzzleStreamTransport;
use HelgeSverre\Synapse\Streaming\TextDelta;

$transport = new GuzzleStreamTransport(new Client(['timeout' => 60]));
$llm = useLlm('openai', [
    'apiKey' => getenv('OPENAI_API_KEY'),
    'model' => 'gpt-4o-mini',
    'transport' => $transport,
]);

$prompt = (new TextPrompt)->setContent('Write a haiku about PHP.');
$executor = new StreamingLlmExecutor($llm, $prompt, 'gpt-4o-mini');

foreach ($executor->stream([]) as $event) {
    if ($event instanceof TextDelta) {
        echo $event->text;
    }
}

See examples/streaming-cli.php and examples/streaming-chat-cli.php for full demos.

Example Index

Key examples to start with:

  • examples/basic-usage.php
  • examples/tool-calling.php
  • examples/streaming-cli.php
  • examples/agentic-agent-cli.php
  • examples/profilinator2000/

Production-oriented patterns:

  • examples/production/retry-and-fallback.php
  • examples/production/safe-tools.php
  • examples/production/persistent-dialogue-redis.php
  • examples/production/http-sse-chat-endpoint.php
  • examples/production/observability-hooks.php
  • examples/production/testing-with-fakes.php
  • examples/production/trace-bridge.php
  • examples/production/checkpoints-and-memory.php
  • examples/production/workflow-engine.php
  • examples/production/evaluation-suite.php

State Management

use HelgeSverre\Synapse\State\{ConversationState, Message, ContextItem};

// Create state
$state = new ConversationState();

// Add messages
$state = $state
    ->withMessage(Message::user('Hello'))
    ->withMessage(Message::assistant('Hi there!'));

// Add context
$state = $state->withContext(new ContextItem('user_id', '12345'));

// Add attributes
$state = $state->withAttribute('session_start', time());

// Use in prompt
$prompt = createChatPrompt()
    ->addSystemMessage('You are helpful.')
    ->addHistoryPlaceholder('history')
    ->addUserMessage('{{message}}', parseTemplate: true);

$result = $executor->run([
    'history' => $state->messages,
    'message' => 'What did I say?',
]);

Event Hooks

use HelgeSverre\Synapse\Hooks\Events\{BeforeProviderCall, AfterProviderCall, OnSuccess, OnError};

$executor
    ->on(BeforeProviderCall::class, fn($e) => logger("Calling {$e->request->model}"))
    ->on(AfterProviderCall::class, fn($e) => logger("Used {$e->response->usage->getTotal()} tokens"))
    ->on(OnSuccess::class, fn($e) => logger("Completed in {$e->durationMs}ms"))
    ->on(OnError::class, fn($e) => logger("Error: {$e->error->getMessage()}"));

Embeddings

use function HelgeSverre\Synapse\useEmbeddings;

$embeddings = useEmbeddings('openai', [
    'apiKey' => getenv('OPENAI_API_KEY'),
]);

$response = $embeddings->embed(
    'The quick brown fox jumps over the lazy dog.',
    'text-embedding-3-small',
);

$vector = $response->getEmbedding();

Providers

useLlm() supports the following provider prefixes:

  • openai.*
  • anthropic.*
  • google.* / gemini.*
  • mistral.*
  • xai.* / grok.*
  • groq.*
  • moonshot.*
  • ollama.* (local; no API key required, defaults to http://localhost:11434/v1)

You can set model either inline (prefix.model) or in options (['model' => '...']). Do not provide conflicting model values in both places.

OpenAI

$llm = useLlm('openai.gpt-4o-mini', [
    'apiKey' => 'sk-...',
    'baseUrl' => 'https://api.openai.com/v1', // optional
]);

Anthropic

$llm = useLlm('anthropic.claude-3-sonnet', [
    'apiKey' => 'sk-ant-...',
]);

Google (Gemini)

$llm = useLlm('google.gemini-1.5-flash', [
    'apiKey' => '...',
]);

Mistral

$llm = useLlm('mistral.mistral-small-latest', [
    'apiKey' => '...',
]);

xAI (Grok)

$llm = useLlm('xai.grok-beta', [
    'apiKey' => '...',
]);

Groq

$llm = useLlm('groq.llama-3.3-70b-versatile', [
    'apiKey' => '...',
]);

Moonshot

$llm = useLlm('moonshot.moonshot-v1-8k', [
    'apiKey' => '...',
]);

Ollama (local)

Run open-weights models locally — no API key required. Synapse uses Ollama's OpenAI-compatible endpoint at http://localhost:11434/v1.

$llm = useLlm('ollama.gemma4:latest');

// Or override the host (remote Ollama):
$llm = useLlm('ollama', [
    'baseUrl' => 'http://gpu-box.local:11434/v1',
    'model'   => 'qwen3.6:latest',
]);

Embeddings work the same way:

$embeddings = useEmbeddings('ollama');
$vec = $embeddings->embed('hello world', 'granite-embedding:latest')->getEmbedding();

See examples/ollama-react-agent.php for a manual ReAct loop with tools running entirely against local Gemma.

Custom Provider

Implement LlmProviderInterface:

use HelgeSverre\Synapse\Provider\{LlmProviderInterface, ProviderCapabilities};
use HelgeSverre\Synapse\Provider\Request\GenerationRequest;
use HelgeSverre\Synapse\Provider\Response\GenerationResponse;

class MyProvider implements LlmProviderInterface
{
    public function generate(GenerationRequest $request): GenerationResponse { ... }
    public function getCapabilities(): ProviderCapabilities { ... }
    public function getName(): string { return 'my-provider'; }
}

Architecture

┌─────────────────────────────────────────────────────────────┐
│                        Executor                              │
│  ┌─────────┐    ┌──────────┐    ┌────────┐    ┌─────────┐  │
│  │ Prompt  │ -> │ Provider │ -> │ Parser │ -> │ Result  │  │
│  └─────────┘    └──────────┘    └────────┘    └─────────┘  │
│       ↑              ↑              ↑                       │
│       │              │              │                       │
│   Template       HTTP Call      Extract                     │
│   Rendering      to LLM API     Structured                  │
│                                 Data                        │
└─────────────────────────────────────────────────────────────┘

Testing

Running Tests

# Run unit tests (default)
composer test
phpunit

# Run integration tests (requires API keys)
phpunit --testsuite=Integration
composer test:integration

# Run all tests
phpunit --testsuite=Unit,Integration
composer test:all

Integration Tests

Integration tests require valid API keys set as environment variables:

  • OPENAI_API_KEY, ANTHROPIC_API_KEY, MISTRAL_API_KEY, MOONSHOT_API_KEY, XAI_API_KEY, GOOGLE_API_KEY, GROQ_API_KEY

Tests will automatically skip if the required API key is not set.

License

MIT

helgesverre/synapse 适用场景与选型建议

helgesverre/synapse 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 3 次下载、GitHub Stars 达 1, 最近一次更新时间为 2026 年 02 月 04 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「parser」 「prompt」 「executor」 「ai」 「openai」 「llm」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 helgesverre/synapse 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 helgesverre/synapse 我们能提供哪些服务?
定制开发 / 二次开发

基于 helgesverre/synapse 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 3
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 1
  • 点击次数: 29
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 1
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-02-04