148 lines
3.3 KiB
PHP
148 lines
3.3 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
namespace Core\Agentic\Models;
|
||
|
|
|
||
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||
|
|
use Illuminate\Database\Eloquent\Model;
|
||
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Workspace State Model
|
||
|
|
*
|
||
|
|
* Key-value state storage for agent plans with typed content.
|
||
|
|
*/
|
||
|
|
class WorkspaceState extends Model
|
||
|
|
{
|
||
|
|
use HasFactory;
|
||
|
|
|
||
|
|
public const TYPE_JSON = 'json';
|
||
|
|
|
||
|
|
public const TYPE_MARKDOWN = 'markdown';
|
||
|
|
|
||
|
|
public const TYPE_CODE = 'code';
|
||
|
|
|
||
|
|
public const TYPE_REFERENCE = 'reference';
|
||
|
|
|
||
|
|
protected $fillable = [
|
||
|
|
'agent_plan_id',
|
||
|
|
'key',
|
||
|
|
'value',
|
||
|
|
'type',
|
||
|
|
'metadata',
|
||
|
|
];
|
||
|
|
|
||
|
|
protected $casts = [
|
||
|
|
'metadata' => 'array',
|
||
|
|
];
|
||
|
|
|
||
|
|
protected $attributes = [
|
||
|
|
'type' => self::TYPE_JSON,
|
||
|
|
'metadata' => '{}',
|
||
|
|
];
|
||
|
|
|
||
|
|
public function plan(): BelongsTo
|
||
|
|
{
|
||
|
|
return $this->belongsTo(AgentPlan::class, 'agent_plan_id');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get typed value.
|
||
|
|
*/
|
||
|
|
public function getTypedValue(): mixed
|
||
|
|
{
|
||
|
|
return match ($this->type) {
|
||
|
|
self::TYPE_JSON => json_decode($this->value, true),
|
||
|
|
default => $this->value,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Set typed value.
|
||
|
|
*/
|
||
|
|
public function setTypedValue(mixed $value): void
|
||
|
|
{
|
||
|
|
$storedValue = match ($this->type) {
|
||
|
|
self::TYPE_JSON => json_encode($value),
|
||
|
|
default => (string) $value,
|
||
|
|
};
|
||
|
|
|
||
|
|
$this->update(['value' => $storedValue]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get or create state for a plan.
|
||
|
|
*/
|
||
|
|
public static function getOrCreate(AgentPlan $plan, string $key, mixed $default = null, string $type = self::TYPE_JSON): self
|
||
|
|
{
|
||
|
|
$state = static::where('agent_plan_id', $plan->id)
|
||
|
|
->where('key', $key)
|
||
|
|
->first();
|
||
|
|
|
||
|
|
if (! $state) {
|
||
|
|
$value = match ($type) {
|
||
|
|
self::TYPE_JSON => json_encode($default),
|
||
|
|
default => (string) ($default ?? ''),
|
||
|
|
};
|
||
|
|
|
||
|
|
$state = static::create([
|
||
|
|
'agent_plan_id' => $plan->id,
|
||
|
|
'key' => $key,
|
||
|
|
'value' => $value,
|
||
|
|
'type' => $type,
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
return $state;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Set state value for a plan.
|
||
|
|
*/
|
||
|
|
public static function set(AgentPlan $plan, string $key, mixed $value, string $type = self::TYPE_JSON): self
|
||
|
|
{
|
||
|
|
$storedValue = match ($type) {
|
||
|
|
self::TYPE_JSON => json_encode($value),
|
||
|
|
default => (string) $value,
|
||
|
|
};
|
||
|
|
|
||
|
|
return static::updateOrCreate(
|
||
|
|
['agent_plan_id' => $plan->id, 'key' => $key],
|
||
|
|
['value' => $storedValue, 'type' => $type]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get state value for a plan.
|
||
|
|
*/
|
||
|
|
public static function get(AgentPlan $plan, string $key, mixed $default = null): mixed
|
||
|
|
{
|
||
|
|
$state = static::where('agent_plan_id', $plan->id)
|
||
|
|
->where('key', $key)
|
||
|
|
->first();
|
||
|
|
|
||
|
|
if (! $state) {
|
||
|
|
return $default;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $state->getTypedValue();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Scope: for plan.
|
||
|
|
*/
|
||
|
|
public function scopeForPlan($query, int $planId)
|
||
|
|
{
|
||
|
|
return $query->where('agent_plan_id', $planId);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Scope: by type.
|
||
|
|
*/
|
||
|
|
public function scopeByType($query, string $type)
|
||
|
|
{
|
||
|
|
return $query->where('type', $type);
|
||
|
|
}
|
||
|
|
}
|