92 lines
3.0 KiB
PHP
92 lines
3.0 KiB
PHP
<?php
|
|
|
|
namespace App\Ai\Agents;
|
|
|
|
use App\Enums\IngredientUnit;
|
|
use App\Enums\MealPostType;
|
|
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
|
use Laravel\Ai\Contracts\Agent;
|
|
use Laravel\Ai\Contracts\Conversational;
|
|
use Laravel\Ai\Contracts\HasStructuredOutput;
|
|
use Laravel\Ai\Contracts\HasTools;
|
|
use Laravel\Ai\Contracts\Tool;
|
|
use Laravel\Ai\Messages\Message;
|
|
use Laravel\Ai\Promptable;
|
|
use Stringable;
|
|
|
|
class MealImageAnalyzer implements Agent, Conversational, HasStructuredOutput, HasTools
|
|
{
|
|
use Promptable;
|
|
|
|
public function instructions(): Stringable|string
|
|
{
|
|
return <<<'INSTRUCTIONS'
|
|
You analyze meal photos for a food tracking app.
|
|
|
|
Return a practical estimate for the visible portion only. Do not invent hidden dishes, drinks, sauces, or sides. If quantities are uncertain, make a reasonable conservative estimate.
|
|
|
|
Use French for title, caption, and ingredient names.
|
|
Use one of these units for ingredients: g, kg, ml, l, piece, tsp, tbsp.
|
|
Use one of these meal types: breakfast, lunch, dinner, brunch, snack, drink, supplement, other.
|
|
Nutrition values must estimate the whole visible meal.
|
|
INSTRUCTIONS;
|
|
}
|
|
|
|
/**
|
|
* @return Message[]
|
|
*/
|
|
public function messages(): iterable
|
|
{
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* @return Tool[]
|
|
*/
|
|
public function tools(): iterable
|
|
{
|
|
return [];
|
|
}
|
|
|
|
public function schema(JsonSchema $schema): array
|
|
{
|
|
return [
|
|
'title' => $schema->string()
|
|
->description('Short French meal title.')
|
|
->required(),
|
|
'caption' => $schema->string()
|
|
->description('One concise French sentence describing the visible meal.')
|
|
->required(),
|
|
'type' => $schema->string()
|
|
->enum(MealPostType::class)
|
|
->description('Best matching meal type.')
|
|
->required(),
|
|
'calories' => $schema->integer()
|
|
->min(0)
|
|
->description('Estimated calories for the visible meal.')
|
|
->required(),
|
|
'proteins' => $schema->number()
|
|
->min(0)
|
|
->description('Estimated proteins in grams.')
|
|
->required(),
|
|
'carbs' => $schema->number()
|
|
->min(0)
|
|
->description('Estimated carbohydrates in grams.')
|
|
->required(),
|
|
'fats' => $schema->number()
|
|
->min(0)
|
|
->description('Estimated fats in grams.')
|
|
->required(),
|
|
'ingredients' => $schema->array()
|
|
->items($schema->object([
|
|
'position' => $schema->integer()->min(1)->required(),
|
|
'ingredient' => $schema->string()->required(),
|
|
'quantity' => $schema->integer()->min(1)->required(),
|
|
'unit' => $schema->string()->enum(IngredientUnit::class)->required(),
|
|
]))
|
|
->description('Visible ingredients ordered by importance.')
|
|
->required(),
|
|
];
|
|
}
|
|
}
|