원클릭으로
llm-integration
Multi-provider LLM integration for phrase generation (Groq, OpenAI, Gemini, extensible)
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Multi-provider LLM integration for phrase generation (Groq, OpenAI, Gemini, extensible)
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Cycle ORM patterns, configuration and common pitfalls for Symfony integration
Docker with multi-stage builds, security best practices and Docker Compose
Symfony framework with PestPHP testing and Clean Architecture patterns
WCAG 2.2 AA compliance for SAAC applications
Symfony with Pest PHP testing framework and TDD patterns
PostgreSQL database configuration and best practices
| name | llm-integration |
| description | Multi-provider LLM integration for phrase generation (Groq, OpenAI, Gemini, extensible) |
| license | MIT |
| compatibility | opencode |
| metadata | {"type":"ai-integration","pattern":"factory"} |
El proyecto usa un patrón Factory para soportar múltiples proveedores LLM de forma intercambiable. El Domain define el contrato, Infrastructure implementa cada provider.
Domain/
Phrase/Service/PhraseGeneratorInterface.php # Contrato agnóstico
Infrastructure/
ExternalApi/
PhraseGeneratorFactory.php # Factory (selecciona provider por env)
Shared/PhrasePrompt.php # Prompt compartido entre providers
OpenAI/
RealOpenAIPhraseGenerator.php # Provider OpenAI-compatible (Groq, OpenAI)
Exception/OpenAIException.php
Gemini/
GeminiPhraseGenerator.php # Provider Gemini (REST API propia)
Exception/GeminiException.php
Phrase/
FakePhraseGenerator.php # Fake (templates sin API, dev/test)
| Provider | Env PHRASE_PROVIDER | Modelo default |
|---|---|---|
| Groq GPT-OSS 120B | openai (API compatible) | openai/gpt-oss-120b |
| Groq Llama 3.3 70B | openai (API compatible) | meta-llama/llama-3.3-70b-versatile |
| OpenAI | openai | gpt-4o-mini |
| Gemini | gemini | gemini-2.5-flash |
| Fake | fake | ninguno (templates locales) |
Nota: Groq usa API compatible OpenAI. Cambiar de OpenAI a Groq (o viceversa) es solo cambiar
OPENAI_BASE_URLyOPENAI_MODELen.env. No requiere cambios de código. Cualquier provider puede usarse en cualquier entorno.
// Domain/Phrase/Service/PhraseGeneratorInterface.php
interface PhraseGeneratorInterface
{
/** @return array<string> List of phrase variations (typically 3) */
public function generate(PictogramSequence $sequence, array $labels): array;
}
El Domain NO conoce que LLM se usa. Solo define el contrato.
// Infrastructure/ExternalApi/PhraseGeneratorFactory.php
public function create(): PhraseGeneratorInterface
{
return match ($this->provider) { // $this->provider viene de env(PHRASE_PROVIDER)
'gemini' => new GeminiPhraseGenerator(...),
'openai' => new RealOpenAIPhraseGenerator(...),
'fake' => new FakePhraseGenerator(),
default => new FakePhraseGenerator(), // Fallback seguro
};
}
Todos los providers usan el mismo prompt (consistencia para usuarios SAAC):
// Infrastructure/ExternalApi/Shared/PhrasePrompt.php
final class PhrasePrompt
{
public const string SYSTEM = <<<PROMPT
Eres un asistente especializado en comunicación aumentativa y alternativa (SAAC).
Tu tarea es convertir palabras clave de pictogramas en frases naturales en español.
Genera exactamente 3 variaciones de la frase.
Responde SOLO con un JSON válido: {"variations": ["frase 1", "frase 2", "frase 3"]}
PROMPT;
public const string USER_TEMPLATE = 'Genera 3 variaciones de frase natural para las siguientes palabras: %s';
public const int VARIATIONS_COUNT = 3;
public const int MAX_LABEL_LENGTH = 50;
}
Infrastructure/ExternalApi/{ProviderName}/PhraseGeneratorInterfaceException/{ProviderName}Exception.phpPhrasePrompt::SYSTEM y PhrasePrompt::USER_TEMPLATEmatch() en PhraseGeneratorFactory.env.example y docker-compose.ymlconfig/packages/services.yaml si necesita argumentos DIfinal class NuevoProvider implements PhraseGeneratorInterface
{
public function __construct(
private readonly HttpClientInterface $httpClient,
private readonly string $apiUrl,
private readonly string $apiKey,
private readonly string $model,
private readonly float $temperature,
private readonly int $maxTokens,
private readonly int $timeout,
) {}
public function generate(PictogramSequence $sequence, array $labels): array
{
// 1. Sanitizar labels (prevenir prompt injection)
// 2. Construir request body (formato específico del provider)
// 3. Enviar request con timeout
// 4. Parsear response JSON: {"variations": [...]}
// 5. Devolver array<string> con max VARIATIONS_COUNT elementos
}
}
.env, NUNCA en código/api/phrases/generate)[^\p{L}\p{N}\s\-])fake como default en desarrollo (no consume API real)# Provider selection
PHRASE_PROVIDER=fake # fake | openai | gemini
# OpenAI-compatible (Groq, OpenAI)
OPENAI_API_URL=https://api.groq.com/openai/v1/chat/completions
OPENAI_API_KEY=gsk_...
OPENAI_MODEL=openai/gpt-oss-120b
# Para usar OpenAI directo en vez de Groq:
# OPENAI_API_URL=https://api.openai.com/v1/chat/completions
# OPENAI_API_KEY=sk-...
# OPENAI_MODEL=gpt-4o-mini
# Gemini
GEMINI_API_URL=https://generativelanguage.googleapis.com/v1beta/models
GEMINI_API_KEY=AIza...
GEMINI_MODEL=gemini-2.5-flash
# Shared
PHRASE_TEMPERATURE=0.7
PHRASE_MAX_TOKENS=2048
PHRASE_TIMEOUT=10
Problema: PHRASE_PROVIDER vacío o no definido.
Solución: El factory hace fallback a fake. En producción, asegurar que está definido explícitamente.
Problema: Request tarda más del timeout configurado. Solución: Configurar timeout adecuado al provider + cache de frases generadas para sequences repetidas.
Problema: Labels maliciosos pueden inyectar instrucciones al LLM.
Solución: Sanitizar con regex [^\p{L}\p{N}\s\-] y limitar a 50 chars (ya implementado en cada provider).