Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions app/Http/Controllers/BillingController.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ class BillingController extends Controller
public function __construct(XenditService $xenditService)
{
$this->xenditService = $xenditService;

if (config('app.edition') !== 'cloud') {
abort(404);
}
}

public function index(Request $request)
Expand All @@ -36,14 +40,15 @@ public function index(Request $request)
'updated_at' => $transaction->updated_at,
'amount' => $transaction->amount,
'type' => $transaction->type,
'transaction_type' => $transaction->transaction_type,
'description' => $transaction->description,
'status' => $currentStatus,
'payment_method' => $transaction->payment_method,
'payment_details' => $transaction->payment_details,
'expired_at' => $transaction->expired_at,
'formatted_status' => ucfirst($currentStatus),
'status_color' => $this->getStatusColor($currentStatus),
'formatted_type' => ucfirst($transaction->type),
'formatted_type' => ucfirst(str_replace('_', ' ', $transaction->type)),
'type_color' => $this->getTypeColor($transaction->type),
];
});
Expand Down Expand Up @@ -103,6 +108,7 @@ public function topup(Request $request)
'team_id' => $team->id,
'amount' => $request->amount,
'type' => 'topup',
'transaction_type' => 'credit',
'description' => 'Credit top-up',
'status' => 'pending',
'external_id' => $externalId,
Expand Down Expand Up @@ -216,7 +222,10 @@ public function handleWebhook(Request $request)
['team_id' => $transaction->team_id],
['amount' => 0]
);
$balance->amount += $payload['amount'];
// Amount is now stored as integer (multiplied by 1,000,000)
// The setAmountAttribute in Balance model will handle the conversion
$currentDecimalAmount = $balance->decimal_amount;
$balance->amount = $currentDecimalAmount + $payload['amount'];
$balance->save();

Log::info('Successfully processed Xendit payment', [
Expand Down
35 changes: 22 additions & 13 deletions app/Http/Controllers/BotController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@
use App\Models\ChatHistory;
use App\Models\Knowledge;
use App\Models\Tool;
use App\Services\AIResponseService;
use App\Services\MessageHandlerService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class BotController extends Controller
{
protected $aiResponseService;
protected $messageHandlerService;

public function __construct(AIResponseService $aiResponseService)
public function __construct(MessageHandlerService $messageHandlerService)
{
$this->authorizeResource(Bot::class);

$this->aiResponseService = $aiResponseService;
$this->messageHandlerService = $messageHandlerService;
}

public function index(Request $request)
Expand Down Expand Up @@ -191,35 +191,44 @@ public function disconnectTool(Request $request, Bot $bot)
public function testMessage(Request $request, Bot $bot)
{
$validated = $request->validate([
'message' => 'required|string|max:1000',
'message' => 'nullable|string|max:1000',
'media_file' => 'nullable|file|max:20480|mimes:jpg,jpeg,png,gif,webp,mp3,wav,ogg,m4a,webm,flac,mp4,avi,mov,pdf,doc,docx,txt',
]);

try {
$response = $this->aiResponseService->generateResponse(
$bot,
$validated['message'],
'testing',
$messageContent = $validated['message'] ?? null;
$mediaFile = $request->hasFile('media_file') ? $request->file('media_file') : null;

// Use handleMessage method with bot parameter for testing
$result = $this->messageHandlerService->handleMessage(
null, // no channel for testing
null,
'html'
'testing',
$messageContent,
$mediaFile,
$bot,
);

$response = $result['response'];
$media = $result['media'];

// Return back with flash data
return back()->with('chatResponse', [
'success' => true,
'response' => $response,
'timestamp' => now()->toISOString(),
'hasMedia' => $media !== null,
]);
} catch (\Exception $e) {
Log::error('Failed to generate test response: ' . $e->getMessage(), [
'bot_id' => $bot->id,
'message' => $validated['message'],
'message' => $validated['message'] ?? null,
'has_media' => $request->hasFile('media_file'),
'exception' => $e->getTraceAsString()
]);

return back()->with('chatResponse', [
'success' => false,
'error' => 'Failed to generate response. Please try again.',
'error' => $e->getMessage(),
'timestamp' => now()->toISOString(),
]);
}
Expand Down
54 changes: 54 additions & 0 deletions app/Http/Controllers/PricingController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

namespace App\Http\Controllers;

use App\Services\TokenPricingService;
use Illuminate\Http\Request;
use Inertia\Inertia;

class PricingController extends Controller
{
protected TokenPricingService $tokenPricingService;

public function __construct(TokenPricingService $tokenPricingService)
{
$this->tokenPricingService = $tokenPricingService;

if (config('app.edition') !== 'cloud') {
abort(404);
}
}

/**
* Display the pricing page with token pricing for all models.
*/
public function index(Request $request)
{
// Get all pricing data
$allPricing = $this->tokenPricingService->getAllPricing();

// Transform pricing data to include both USD and credits
$pricingData = [];

foreach ($allPricing as $provider => $models) {
$pricingData[$provider] = [];

foreach ($models as $modelName => $pricing) {
$pricingData[$provider][$modelName] = [
'input_usd' => $pricing['input'],
'output_usd' => $pricing['output'],
'input_credits' => $this->tokenPricingService->usdToCredits($pricing['input']),
'output_credits' => $this->tokenPricingService->usdToCredits($pricing['output']),
];
}
}

return Inertia::render('Pricing/Index', [
'pricing' => $pricingData,
'exchange_rate' => [
'usd_to_credits' => 16500,
'credits_to_usd' => 1 / 16500,
],
]);
}
}
106 changes: 106 additions & 0 deletions app/Http/Controllers/UsageController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php

namespace App\Http\Controllers;

use App\Models\TokenUsage;
use Illuminate\Http\Request;
use Inertia\Inertia;

class UsageController extends Controller
{
public function __construct()
{
if (config('app.edition') !== 'cloud') {
abort(404);
}
}

/**
* Display the usage page with token usage statistics.
*/
public function index(Request $request)
{
$team = $request->user()->currentTeam;

// Get token usage data for the current team
$usages = TokenUsage::where('team_id', $team->id)
->with('bot:id,name')
->orderBy('id', 'desc')
->paginate(50);

// Calculate summary statistics
$totalCreditsRaw = TokenUsage::where('team_id', $team->id)->sum('credits');
$totalCredits = $totalCreditsRaw / 1000000; // Convert from integer to decimal
$totalInputTokens = TokenUsage::where('team_id', $team->id)->sum('input_tokens');
$totalOutputTokens = TokenUsage::where('team_id', $team->id)->sum('output_tokens');

// Get usage by provider
$usageByProvider = TokenUsage::where('team_id', $team->id)
->selectRaw('provider, SUM(credits) as total_credits, SUM(input_tokens) as total_input_tokens, SUM(output_tokens) as total_output_tokens')
->groupBy('provider')
->get()
->map(function ($item) {
$item->total_input_tokens = (int) $item->total_input_tokens;
$item->total_output_tokens = (int) $item->total_output_tokens;
$item->total_credits = (float) ($item->total_credits / 1000000); // Convert from integer to decimal
return $item;
});

// Get usage by model
$usageByModel = TokenUsage::where('team_id', $team->id)
->selectRaw('provider, model, SUM(credits) as total_credits, SUM(input_tokens) as total_input_tokens, SUM(output_tokens) as total_output_tokens, COUNT(*) as usage_count')
->groupBy('provider', 'model')
->orderBy('total_credits', 'desc')
->get()
->map(function ($item) {
$item->total_input_tokens = (int) $item->total_input_tokens;
$item->total_output_tokens = (int) $item->total_output_tokens;
$item->total_credits = (float) ($item->total_credits / 1000000); // Convert from integer to decimal
$item->usage_count = (int) $item->usage_count;
return $item;
});

// Get usage by bot
$usageByBot = TokenUsage::where('team_id', $team->id)
->with('bot:id,name')
->selectRaw('bot_id, SUM(credits) as total_credits, SUM(input_tokens) as total_input_tokens, SUM(output_tokens) as total_output_tokens, COUNT(*) as usage_count')
->groupBy('bot_id')
->orderBy('total_credits', 'desc')
->get()
->map(function ($item) {
$item->total_input_tokens = (int) $item->total_input_tokens;
$item->total_output_tokens = (int) $item->total_output_tokens;
$item->total_credits = (float) ($item->total_credits / 1000000); // Convert from integer to decimal
$item->usage_count = (int) $item->usage_count;
return $item;
});

// Get daily usage for the last 30 days
$dailyUsage = TokenUsage::where('team_id', $team->id)
->selectRaw('DATE(created_at) as date, SUM(credits) as total_credits, SUM(input_tokens) as total_input_tokens, SUM(output_tokens) as total_output_tokens')
->where('created_at', '>=', now()->subDays(30))
->groupBy('date')
->orderBy('date', 'desc')
->get()
->map(function ($item) {
$item->total_input_tokens = (int) $item->total_input_tokens;
$item->total_output_tokens = (int) $item->total_output_tokens;
$item->total_credits = (float) ($item->total_credits / 1000000); // Convert from integer to decimal
return $item;
});

return Inertia::render('Usage/Index', [
'usages' => $usages,
'summary' => [
'total_credits' => round($totalCredits, 2),
'total_input_tokens' => $totalInputTokens,
'total_output_tokens' => $totalOutputTokens,
'total_tokens' => $totalInputTokens + $totalOutputTokens,
],
'usage_by_provider' => $usageByProvider,
'usage_by_model' => $usageByModel,
'usage_by_bot' => $usageByBot,
'daily_usage' => $dailyUsage,
]);
}
}
63 changes: 19 additions & 44 deletions app/Http/Controllers/WhatsAppMessageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,35 @@

namespace App\Http\Controllers;

use App\Models\ChatHistory;
use App\Models\Channel;
use App\Services\AIResponseService;
use App\Services\MediaProcessingService;
use App\Services\TransactionService;
use App\Services\MessageHandlerService;
use Illuminate\Http\Request;

class WhatsAppMessageController extends Controller
{
protected $aiResponseService;
protected $transactionService;
protected $mediaProcessingService;

public function __construct(
AIResponseService $aiResponseService,
TransactionService $transactionService,
MediaProcessingService $mediaProcessingService
) {
$this->aiResponseService = $aiResponseService;
$this->transactionService = $transactionService;
$this->mediaProcessingService = $mediaProcessingService;
protected $messageHandlerService;

public function __construct(MessageHandlerService $messageHandlerService)
{
$this->messageHandlerService = $messageHandlerService;
}

public function handleIncomingMessage(Request $request)
{
$validated = $request->validate([
'channelId' => 'required|integer',
'sender' => 'required|string',
'message' => 'nullable|string',
'media_file' => 'nullable|file|max:20480|mimes:jpg,jpeg,png,gif,webp,mp3,wav,ogg,m4a,webm,flac,mp4,avi,mov,pdf,doc,docx,txt',
]);

$channelId = $validated['channelId'];
$sender = $validated['sender'];
$messageContent = $validated['message'];

$media = null;
if ($request->hasFile('media_file')) {
$media = $this->mediaProcessingService->process($request->file('media_file'), $messageContent);
}

$channel = Channel::with(['bots', 'team.balance'])->findOrFail($channelId);
$bot = $channel->bots->first();
try {
// Validate the request data
$validated = $this->messageHandlerService->validateMessageData($request->all());

if (!$bot) {
return response()->json(['error' => 'No bot found for this channel'], 404);
}

$response = $this->aiResponseService->generateResponse($bot, $messageContent, $sender, $channelId, $media);
$channelId = $validated['channelId'];
$sender = $validated['sender'];
$messageContent = $validated['message'] ?? null;
$mediaFile = $request->hasFile('media_file') ? $request->file('media_file') : null;

// Record usage transaction and deduct credits
$this->transactionService->recordUsage($channel->team);
// Handle the message using the service
$result = $this->messageHandlerService->handleMessage($channelId, $sender, $messageContent, $mediaFile, null, 'whatsapp');

return response()->json(['response' => $response]);
return response()->json(['response' => $result['response']]);
} catch (\Exception $e) {
return response()->json(['error' => $e->getMessage()], 404);
}
}

}
1 change: 1 addition & 0 deletions app/Http/Middleware/HandleInertiaRequests.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public function share(Request $request): array

return array_merge(parent::share($request), [
'flash' => $session->all(),
'appEdition' => config('app.edition'),
]);
}
}
Loading