HotelHub HMSDocs

05 β€” AI Providers #

11 preset AI provider termurah untuk BYOK integration. Bukan hardcoded di code β€” disimpan sebagai JSON template di storage/app/llm-presets/ untuk autofill convenience saja.


0. Hard Rule (no hardcoded providers) #

JANGAN PERNAH:

WAJIB:

Sesuai global preferences user β€” pattern ini berlaku global di semua project.


1. Daftar 11 Preset (cheapest BYOK options) #

Harga dalam USD per 1 juta token (estimate snapshot 2026-04-28; akan bergeser, tapi preset hanya autofill, user koreksi di UI).

# Provider Format Best-cheap model Input $/1M Output $/1M Catatan
1 Self-hosted (Ollama / LM Studio / vLLM) OpenAI-compat Llama 3.3 70B, Qwen 2.5 72B, Phi-4 $0 $0 Hardware cost only. Full data sovereignty.
2 DeepSeek OpenAI-compat deepseek-chat, deepseek-reasoner $0.27 $1.10 Reasoning model murah
3 Mistral La Plateforme OpenAI-compat ministral-3b-latest $0.04 $0.04 Termurah dari big provider
4 Google Gemini Flash Gemini gemini-2.5-flash, gemini-2.5-flash-lite $0.075 $0.30 Multimodal (image, audio)
5 DeepInfra OpenAI-compat Llama 3.3 70B, Qwen 2.5 72B $0.08 $0.40 Banyak open-source model
6 Hyperbolic OpenAI-compat Llama 3.3 70B, Qwen $0.10 $0.40 Cheap inference
7 Together AI OpenAI-compat Llama 3.3, DeepSeek V3, Qwen $0.18 $0.88 Free tier untuk dev
8 Fireworks AI OpenAI-compat Llama 3.3, DeepSeek, Qwen $0.20 $0.90 Tuning support
9 Groq OpenAI-compat llama-3.3-70b-versatile $0.59 $0.79 Fastest inference (>500 tok/s)
10 OpenRouter OpenAI-compat auto-router (cheapest) varies varies Single key untuk 100+ model
11 Anthropic Anthropic claude-haiku-4-5 ~$1.00 ~$5.00 Premium budget β€” quality terbaik untuk concierge

Kenapa 11, bukan 10: user request tambah Anthropic. Anthropic Haiku 4.5 lebih mahal dari yang lain di list, tapi quality untuk concierge multi-bahasa (terutama Bahasa Indonesia + bahasa tamu Asing) lebih baik secara umum. Owner bisa mix: pakai DeepSeek/Gemini Flash untuk volume rendah biaya, Anthropic Haiku untuk customer-facing premium.

Owner bebas swap, add custom provider, atau skip. UI dropdown isi cuma yang owner tambah; default empty.


2. JSON Preset Format #

storage/app/llm-presets/*.json:

{
  "preset_id": "deepseek",
  "display_name": "DeepSeek",
  "api_format": "openai_compatible",
  "base_url": "https://api.deepseek.com/v1",
  "auth_header": "Authorization",
  "auth_prefix": "Bearer ",
  "models_endpoint": "/models",
  "chat_endpoint": "/chat/completions",
  "default_model": "deepseek-chat",
  "supported_models": [
    {
      "id": "deepseek-chat",
      "context": 64000,
      "input_price_per_1m_usd": 0.27,
      "output_price_per_1m_usd": 1.10,
      "supports_vision": false,
      "supports_function_calling": true
    },
    {
      "id": "deepseek-reasoner",
      "context": 64000,
      "input_price_per_1m_usd": 0.55,
      "output_price_per_1m_usd": 2.19,
      "supports_vision": false,
      "supports_function_calling": false
    }
  ],
  "docs_url": "https://api-docs.deepseek.com",
  "free_tier": false,
  "indonesia_friendly": true,
  "notes": "OpenAI-compatible. No regional restriction. Supports streaming."
}

Owner buka admin β†’ Integrations β†’ AI β†’ "Add Provider" β†’ pick preset β†’ wizard isi field β†’ manual edit kalau perlu β†’ save (api_key encrypted).


3. Adapter Contract #

<?php

namespace App\Modules\Ai\Adapters;

interface AiAdapterInterface
{
    public function chat(array $messages, array $options = []): AiChatResponse;
    public function listModels(): array;
    public function tokenCount(string $text): int;
    public function supportsVision(): bool;
    public function supportsFunctionCalling(): bool;
}

final readonly class AiChatResponse
{
    public function __construct(
        public string $content,
        public string $model,
        public int $inputTokens,
        public int $outputTokens,
        public ?string $finishReason = null,
        public array $toolCalls = [],
        public array $rawResponse = [],
    ) {}
}

Implementations:

Adapter class Covers preset #
OpenAICompatibleAdapter 1, 2, 3, 5, 6, 7, 8, 9, 10
GeminiFormatAdapter 4
AnthropicFormatAdapter 11

3 adapter classes total untuk cover 11 provider. Pattern hemat.


4. Use Cases di Hotel System #

Semua opsional. Owner aktifkan per-fitur dan pilih provider.

A. AI Concierge Chatbot (guest-facing) #

B. Auto-translate room/property descriptions #

C. pSEO content generator #

D. Smart email/WhatsApp reply suggestion #

E. Auto-reply review (Booking, Agoda, Google) #

F. KTP/Paspor OCR (vision LLM) #

G. Demand forecasting #

H. Sentiment analysis review #

I. Voice-to-reservation (call center, Phase 3) #


5. Configuration UI Flow #

Admin β†’ Integrations β†’ AI Providers
    ↓
[+ Add Provider]
    ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Pick a preset (or skip):             β”‚
β”‚  β—‹ Self-hosted (Ollama / LM Studio)  β”‚
β”‚  β—‹ DeepSeek                          β”‚
β”‚  β—‹ Mistral La Plateforme             β”‚
β”‚  β—‹ Google Gemini Flash               β”‚
β”‚  β—‹ DeepInfra                         β”‚
β”‚  β—‹ Hyperbolic                        β”‚
β”‚  β—‹ Together AI                       β”‚
β”‚  β—‹ Fireworks AI                      β”‚
β”‚  β—‹ Groq                              β”‚
β”‚  β—‹ OpenRouter                        β”‚
β”‚  β—‹ Anthropic                         β”‚
β”‚  β—‹ Custom (input semua manual)       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    ↓
Wizard fields (autofill dari preset, editable):
  Name (display): [DeepSeek                ]
  Base URL:       [https://api.deepseek.com/v1]
  API Format:     [openai_compatible v]
  API Key:        [sk-xxxxx                ] (encrypted)
  Default Model:  [deepseek-chat           ]
  Extra Headers:  (JSON, optional)
  Notes:          (free text)
    ↓
[Test Connection] β†’ ping /models endpoint
    ↓
[Save] β†’ Provider record created (api_key encrypted)
    ↓
Per-feature mapping:
  Concierge:        [DeepSeek         v]
  Translation:      [Mistral          v]
  pSEO content:     [DeepSeek         v]
  Email suggestion: [β€”not configuredβ€”]
  OCR fallback:     [Gemini Flash     v]
  ...

6. Auto-fetch Models #

Setelah save provider, button "Fetch Models from /models endpoint" tarik daftar model live β†’ owner pilih default. Convenience, tidak wajib.

GET {base_url}/models
Authorization: Bearer {api_key}

β†’ 200 OK
{
  "data": [
    {"id": "deepseek-chat", "object": "model", ...},
    {"id": "deepseek-reasoner", ...}
  ]
}

Untuk provider non-OpenAI-compat, button labeled per spec masing-masing.


7. Cost Tracking #

Tabel ai_usage_logs:

id | provider_id | model | feature | input_tokens | output_tokens | cost_usd | cost_idr | request_id | created_at

Per request, hitung cost_usd = (input_tokens Γ— input_price + output_tokens Γ— output_price) / 1_000_000. Konversi ke IDR pakai exchange rate harian (BYOK exchange API atau hardcoded weekly update).

Dashboard: usage per feature, per provider, per model, per period. Owner pantau biaya real.


8. Rate Limiting & Quota #

Per-provider:

Per-feature:


9. Streaming #

Semua adapter support streaming chat.completions (SSE). Penting untuk concierge UX (typing indicator effect).

$adapter->chatStream($messages, function ($chunk) {
    // emit ke WebSocket / Pusher
    broadcast(new AiChunk($conversationId, $chunk));
});

10. Fallback Strategy #

Owner bisa set primary + fallback provider per fitur.

Concierge: primary=Anthropic Haiku, fallback=DeepSeek
    ↓
Try Anthropic
  β”œβ”€ 5xx atau timeout β†’ switch ke DeepSeek
  └─ Hard error β†’ log + return generic "Sorry, having trouble" message

11. Self-host Option (Provider #1) #

Owner punya GPU sendiri (RTX 4090 / 3090 / Mac M-series) bisa run Ollama/LM Studio/vLLM lokal:

# Ollama install
curl https://ollama.ai/install.sh | sh

# Pull model
ollama pull llama3.3:70b
ollama pull qwen2.5:72b

# Start server (default port 11434)
ollama serve

# Endpoint OpenAI-compatible
# http://localhost:11434/v1

Provider config:

Zero biaya per token. Cocok untuk hotel resort yang butuh AI tapi internet kuota terbatas / data sensitif.


12. Encryption at Rest #

// Migration
Schema::create('providers', function (Blueprint $table) {
    $table->id();
    $table->string('integration_type'); // 'ai', 'payment', 'sms', etc.
    $table->string('name');
    $table->string('api_format');
    $table->string('base_url')->nullable();
    $table->text('api_key_encrypted')->nullable();
    $table->json('extra_headers')->nullable();
    $table->string('default_model')->nullable();
    $table->boolean('is_active')->default(false);
    $table->timestamps();
});

// Eloquent accessor
public function getApiKeyAttribute(): ?string
{
    return $this->api_key_encrypted
        ? Crypt::decryptString($this->api_key_encrypted)
        : null;
}

public function setApiKeyAttribute(?string $value): void
{
    $this->attributes['api_key_encrypted'] = $value
        ? Crypt::encryptString($value)
        : null;
}

API response (admin GET) NEVER expose api_key. Hanya field has_api_key: true/false + last-4-char display.


13. Audit Log #

Setiap provider add/edit/delete/test:

audit_logs: action='ai_provider.created'
            actor_id=42
            target_type='provider'
            target_id=7
            meta={"name":"DeepSeek","format":"openai_compatible"}
            // api_key tidak pernah di-log

14. Migrasi Dari Hardcoded β†’ Dynamic #

Kalau ada code yang masih hardcode (misal di sketch awal if ($provider === 'deepseek')), refactor:

❌ Sebelum:

if ($provider === 'deepseek') {
    return new DeepSeekClient($apiKey);
} elseif ($provider === 'openai') {
    return new OpenAIClient($apiKey);
}

βœ… Sesudah:

$adapter = AdapterFactory::for($provider->api_format);
$adapter->configure($provider->base_url, $provider->api_key, ...);
return $adapter;

15. References #

Reference implementation pattern: D:\project laravel\foodscan\docs\10-AI-PROVIDERS.md.