Save workspace changes
This commit is contained in:
@@ -107,8 +107,8 @@ it('can analyze artwork ai suggestions directly without queueing', function ():
|
||||
->assertJsonPath('data.status', ArtworkAiAssist::STATUS_READY)
|
||||
->assertJsonPath('data.debug.request.hash', 'syncaa112233')
|
||||
->assertJsonPath('data.debug.request.intent', 'title')
|
||||
->assertJsonPath('data.debug.vision_debug.image_url', 'https://files.local/md/sy/nc/syncaa112233.webp')
|
||||
->assertJsonPath('data.debug.vision_debug.calls.0.request.image_url', 'https://files.local/md/sy/nc/syncaa112233.webp')
|
||||
->assertJsonPath('data.debug.vision_debug.image_url', 'https://files.local/artworks/md/sy/nc/syncaa112233.webp')
|
||||
->assertJsonPath('data.debug.vision_debug.calls.0.request.image_url', 'https://files.local/artworks/md/sy/nc/syncaa112233.webp')
|
||||
->assertJsonPath('data.debug.vision_debug.calls.0.service', 'gateway_all');
|
||||
|
||||
Queue::assertNotPushed(AnalyzeArtworkAiAssistJob::class);
|
||||
@@ -127,6 +127,124 @@ it('can analyze artwork ai suggestions directly without queueing', function ():
|
||||
expect($completedEvent?->meta['intent'] ?? null)->toBe('title');
|
||||
});
|
||||
|
||||
it('accepts a together provider override for direct studio ai analysis', function (): void {
|
||||
Queue::fake();
|
||||
|
||||
config()->set('vision.enabled', true);
|
||||
config()->set('vision.gateway.base_url', 'https://vision.local');
|
||||
config()->set('vision.together.base_url', 'https://api.together.xyz');
|
||||
config()->set('vision.together.endpoint', '/v1/chat/completions');
|
||||
config()->set('vision.together.model', 'meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo');
|
||||
config()->set('vision.together.api_key', 'together-test-key');
|
||||
config()->set('cdn.files_url', 'https://files.local');
|
||||
|
||||
$photography = ContentType::query()->create([
|
||||
'name' => 'Photography',
|
||||
'slug' => 'photography',
|
||||
]);
|
||||
Category::query()->create([
|
||||
'content_type_id' => $photography->id,
|
||||
'name' => 'Flowers',
|
||||
'slug' => 'flowers',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$user = User::factory()->create();
|
||||
$artwork = Artwork::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'hash' => 'togaa112233',
|
||||
'file_name' => 'rose-closeup.jpg',
|
||||
]);
|
||||
|
||||
Http::fake([
|
||||
'https://vision.local/analyze/all' => Http::response([
|
||||
'clip' => [
|
||||
['tag' => 'rose', 'confidence' => 0.96],
|
||||
['tag' => 'flower', 'confidence' => 0.91],
|
||||
],
|
||||
'yolo' => [
|
||||
['label' => 'flower', 'confidence' => 0.79],
|
||||
],
|
||||
'blip' => 'a close up photograph of a rose bud with soft natural background',
|
||||
], 200),
|
||||
'https://api.together.xyz/v1/chat/completions' => Http::response([
|
||||
'choices' => [
|
||||
[
|
||||
'message' => [
|
||||
'content' => json_encode([
|
||||
'rose macro',
|
||||
'flower close-up',
|
||||
'soft petals',
|
||||
'natural light',
|
||||
'botanical photography',
|
||||
'pink tones',
|
||||
'shallow depth',
|
||||
'floral detail',
|
||||
'macro photography',
|
||||
'garden bloom',
|
||||
], JSON_THROW_ON_ERROR),
|
||||
],
|
||||
],
|
||||
],
|
||||
], 200),
|
||||
]);
|
||||
|
||||
actingAs($user);
|
||||
|
||||
postJson('/api/studio/artworks/' . $artwork->id . '/ai/analyze', [
|
||||
'direct' => true,
|
||||
'provider' => 'together',
|
||||
'intent' => 'tags',
|
||||
])
|
||||
->assertOk()
|
||||
->assertJsonPath('direct', true)
|
||||
->assertJsonPath('data.debug.request.provider', 'together')
|
||||
->assertJsonPath('data.debug.tag_generation.provider', 'together')
|
||||
->assertJsonPath('data.debug.tag_generation.endpoint', 'https://api.together.xyz/v1/chat/completions');
|
||||
|
||||
Http::assertSent(function (\Illuminate\Http\Client\Request $request): bool {
|
||||
return $request->url() === 'https://api.together.xyz/v1/chat/completions'
|
||||
&& $request->hasHeader('Authorization', 'Bearer together-test-key');
|
||||
});
|
||||
|
||||
Queue::assertNotPushed(AnalyzeArtworkAiAssistJob::class);
|
||||
});
|
||||
|
||||
it('passes a provider override into queued studio ai analysis', function (): void {
|
||||
Queue::fake();
|
||||
|
||||
$user = User::factory()->create();
|
||||
$artwork = Artwork::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'hash' => 'queueprov1122',
|
||||
]);
|
||||
|
||||
actingAs($user);
|
||||
|
||||
postJson('/api/studio/artworks/' . $artwork->id . '/ai/analyze', [
|
||||
'provider' => 'together',
|
||||
'intent' => 'tags',
|
||||
])
|
||||
->assertStatus(202)
|
||||
->assertJsonPath('status', ArtworkAiAssist::STATUS_QUEUED);
|
||||
|
||||
Queue::assertPushed(AnalyzeArtworkAiAssistJob::class, function (AnalyzeArtworkAiAssistJob $job): bool {
|
||||
$property = new \ReflectionProperty($job, 'provider');
|
||||
$property->setAccessible(true);
|
||||
|
||||
return $property->getValue($job) === 'together';
|
||||
});
|
||||
|
||||
$requestedEvent = ArtworkAiAssistEvent::query()
|
||||
->where('artwork_id', $artwork->id)
|
||||
->where('event_type', 'analysis_requested')
|
||||
->latest('id')
|
||||
->first();
|
||||
|
||||
expect($requestedEvent)->not->toBeNull();
|
||||
expect($requestedEvent?->meta['provider'] ?? null)->toBe('together');
|
||||
});
|
||||
|
||||
it('persists upload-style visibility options from studio save', function (): void {
|
||||
$user = User::factory()->create();
|
||||
$artwork = Artwork::factory()->create([
|
||||
@@ -269,6 +387,8 @@ it('can analyze artwork directly when exact and vector similar matches are both
|
||||
it('builds and exposes normalized studio ai suggestions', function (): void {
|
||||
config()->set('vision.enabled', true);
|
||||
config()->set('vision.gateway.base_url', 'https://vision.local');
|
||||
config()->set('vision.lm_studio.base_url', 'https://lmstudio.local');
|
||||
config()->set('vision.lm_studio.model', 'google/gemma-3-4b');
|
||||
config()->set('cdn.files_url', 'https://files.local');
|
||||
|
||||
$photography = ContentType::query()->create([
|
||||
@@ -302,6 +422,26 @@ it('builds and exposes normalized studio ai suggestions', function (): void {
|
||||
],
|
||||
'blip' => 'a close up photograph of a rose bud with soft natural background',
|
||||
], 200),
|
||||
'https://lmstudio.local/v1/chat/completions' => Http::response([
|
||||
'choices' => [
|
||||
[
|
||||
'message' => [
|
||||
'content' => json_encode([
|
||||
'rose macro',
|
||||
'flower close-up',
|
||||
'soft petals',
|
||||
'natural light',
|
||||
'botanical photography',
|
||||
'pink tones',
|
||||
'shallow depth',
|
||||
'floral detail',
|
||||
'macro photography',
|
||||
'garden bloom',
|
||||
], JSON_THROW_ON_ERROR),
|
||||
],
|
||||
],
|
||||
],
|
||||
], 200),
|
||||
]);
|
||||
|
||||
app(StudioAiAssistService::class)->analyze($artwork->fresh(), false);
|
||||
@@ -314,9 +454,10 @@ it('builds and exposes normalized studio ai suggestions', function (): void {
|
||||
->assertJsonPath('data.mode', 'artwork')
|
||||
->assertJsonPath('data.content_type.value', 'photography')
|
||||
->assertJsonPath('data.category.value', 'flowers')
|
||||
->assertJsonPath('data.debug.tag_generation.raw_content', '["rose macro","flower close-up","soft petals","natural light","botanical photography","pink tones","shallow depth","floral detail","macro photography","garden bloom"]')
|
||||
->assertJson(fn ($json) => $json
|
||||
->has('data.title_suggestions', 5)
|
||||
->where('data.tag_suggestions.0.tag', 'rose')
|
||||
->where('data.tag_suggestions', fn ($tags): bool => collect($tags)->contains(fn (array $row): bool => ($row['tag'] ?? null) === 'rose-macro'))
|
||||
->has('data.description_suggestions', 3));
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user