Skip to content

Commit 083b281

Browse files
committed
publish improvements
1 parent ca9bbba commit 083b281

8 files changed

Lines changed: 150 additions & 48 deletions

File tree

MyApp.ServiceInterface/AgentEventsManager.cs

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,25 @@ public class AgentEventsManager(ILogger<AgentEventsManager> log, AppData appData
1919

2020
public void QueuePublishedMedia(PublishedMedia media, string userId)
2121
{
22-
if (media.Caption == null)
22+
if (media.Type == AssetType.Image)
2323
{
24-
EnqueueMediaChat(media, CaptionMediaCommand.Prompt, callback:nameof(CaptionMediaCommand), userId:userId);
24+
if (media.Caption == null)
25+
{
26+
EnqueueMediaImageChat(media, CaptionMediaCommand.ImagePrompt, callback:nameof(CaptionMediaCommand), userId:userId);
27+
}
28+
if (media.Description == null)
29+
{
30+
EnqueueMediaImageChat(media, DescribeMediaCommand.Prompt, callback:nameof(DescribeMediaCommand), userId:userId);
31+
}
2532
}
26-
if (media.Description == null)
33+
else if (media.Type == AssetType.Audio)
2734
{
28-
EnqueueMediaChat(media, DescribeMediaCommand.Prompt, callback:nameof(DescribeMediaCommand), userId:userId);
35+
if (media.Caption == null)
36+
{
37+
EnqueueMediaAudioChat(media, CaptionMediaCommand.AudioPrompt + media.Prompt, callback:nameof(CaptionMediaCommand), userId:userId);
38+
}
2939
}
30-
40+
3141
var artifactRefId = media.Id * -1;
3242
QueuedArtifactRefs[artifactRefId] = new ArtifactRef
3343
{
@@ -276,7 +286,7 @@ public BackgroundJobRef EnqueueArtifactChat(Artifact artifact, string prompt, st
276286
return jobRef;
277287
}
278288

279-
public BackgroundJobRef EnqueueMediaChat(PublishedMedia media, string prompt, string? callback = null, string? userId = null, string? model = null)
289+
public BackgroundJobRef EnqueueMediaImageChat(PublishedMedia media, string prompt, string? callback = null, string? userId = null, string? model = null)
280290
{
281291
model ??= appData.Config.VisualLanguageModel;
282292
var taskId = PreciseTimestamp.UniqueUtcNowTicks;
@@ -305,6 +315,35 @@ public BackgroundJobRef EnqueueMediaChat(PublishedMedia media, string prompt, st
305315
});
306316
return jobRef;
307317
}
318+
319+
public BackgroundJobRef EnqueueMediaAudioChat(PublishedMedia media, string prompt, string? callback = null, string? userId = null, string? model = null)
320+
{
321+
model ??= appData.Config.VisualLanguageModel;
322+
var taskId = PreciseTimestamp.UniqueUtcNowTicks;
323+
var refId = $"{taskId}";
324+
var replyTo = $"/api/{nameof(CompleteChatCompletion)}".AddQueryParam(nameof(refId), refId);
325+
var jobRef = jobs.EnqueueCommand<ChatCompletionCommand>(new ChatCompletion {
326+
Model = model,
327+
Messages = [
328+
new()
329+
{
330+
Role = "user",
331+
Content = [
332+
AiContent.Text(prompt)
333+
]
334+
},
335+
]
336+
}, new() {
337+
RefId = refId,
338+
ReplyTo = replyTo,
339+
Callback = callback,
340+
UserId = userId,
341+
Args = new() {
342+
["mediaId"] = $"{media.Id}",
343+
},
344+
});
345+
return jobRef;
346+
}
308347

309348
public void Reload(IDbConnection db)
310349
{

MyApp.ServiceInterface/Commands/OpenAiChatCommand.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,12 @@ protected override async Task RunAsync(ChatResponse request, CancellationToken t
164164
}
165165
}
166166

167-
168167
public class CaptionMediaCommand(
169168
ILogger<CaptionMediaCommand> logger, IBackgroundJobs jobs, IDbConnectionFactory dbFactory)
170169
: AsyncCommand<ChatResponse>
171170
{
172-
public const string Prompt = "Caption this picture in 1 short sentence";
171+
public const string ImagePrompt = "Caption this picture in 1 short sentence";
172+
public const string AudioPrompt = "Generate a short title for this audio generation prompt. The title should be suitable for use as a filename or a short description of the audio content. The title should be less than 8 words and describe the audio content in a concise way. Don't generate multiple options, just a single title. This is the prompt:\n\n";
173173

174174
protected override async Task RunAsync(ChatResponse request, CancellationToken token)
175175
{

MyApp.ServiceInterface/PublishServices.cs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -320,16 +320,9 @@ public async Task<QueryResponse<MediaInfo>> Get(QueryPublishedMedia request)
320320
}
321321
if (request.Tag != null)
322322
{
323-
var tag = appData.GetTag(request.Tag);
324-
if (tag != null)
325-
{
326-
var tagColumn = q.Column<PublishedMedia>(x => x.Tags);
327-
q.And(tagColumn + " ? {0}", tag.Name);
328-
}
329-
else
330-
{
331-
log.LogWarning("Unknown tag {Tag}", request.Tag);
332-
}
323+
//var tag = appData.GetTag(request.Tag);
324+
var tagColumn = q.Column<PublishedMedia>(x => x.Tags);
325+
q.And(tagColumn + " ? {0}", request.Tag);
333326
}
334327

335328
if (request.User != null)

MyApp.ServiceModel/Publish.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ public class MediaInfo
310310
{
311311
public int Id { get; set; }
312312
public string Name { get; set; }
313+
public string? Caption { get; set; }
313314
public AssetType Type { get; set; }
314315
public int Width { get; set; }
315316
public int Height { get; set; }

MyApp/wwwroot/css/app.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,8 +1293,8 @@
12931293
.min-h-0 {
12941294
min-height: calc(var(--spacing) * 0);
12951295
}
1296-
.min-h-\[3rem\] {
1297-
min-height: 3rem;
1296+
.min-h-\[4rem\] {
1297+
min-height: 4rem;
12981298
}
12991299
.min-h-\[350px\] {
13001300
min-height: 350px;

MyApp/wwwroot/llms/media.mjs

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -488,13 +488,14 @@ const App = {
488488
</div>
489489
</div>
490490
<!-- Categories Section -->
491-
<div v-for="(score, category) in parsedMedia.categories ?? {}"
491+
<a v-for="(score, category) in parsedMedia.categories ?? {}"
492492
:key="'cat-' + category"
493+
:href="categoryHref(category)"
493494
class="group cursor-pointer relative inline-flex items-center rounded-full overflow-hidden px-3 py-1 text-xs font-medium ring-1 ring-inset hover:dark:bg-blue-900 hover:dark:ring-blue-500/80"
494-
:class="score
495-
? 'text-blue-800 dark:text-blue-200 ring-blue-600/20 dark:ring-blue-400/30'
495+
:class="score
496+
? 'text-blue-800 dark:text-blue-200 ring-blue-600/20 dark:ring-blue-400/30'
496497
: 'bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-200 ring-blue-600/20 dark:ring-blue-400/30'"
497-
:title="'Score: ' + (score ? Math.round(score * 100) + '%' : 'No score')">
498+
:title="'Search category: ' + category + (score ? ' · Score: ' + Math.round(score * 100) + '%' : '')">
498499
<!-- Background fill based on score -->
499500
<div v-if="score"
500501
class="group-hover:hidden absolute inset-0 bg-gradient-to-r from-blue-300 to-blue-400 dark:from-blue-700 dark:to-blue-800"
@@ -504,20 +505,21 @@ const App = {
504505
class="group-hover:hidden absolute inset-0 bg-blue-100 dark:bg-blue-900/30"></div>
505506
<!-- Text content -->
506507
<span class="relative z-10">{{ category }}</span>
507-
</div>
508+
</a>
508509
</div>
509510
</div>
510511
511-
<!-- Tags Section -->
512-
<div v-if="Object.keys(parsedMedia.tags ?? {}).length">
512+
<!-- Tags Section (ordered by strength) -->
513+
<div v-if="sortedTags.length">
513514
<div class="flex flex-wrap gap-2">
514-
<div v-for="(score, tag) in parsedMedia.tags ?? {}"
515+
<a v-for="[tag, score] in sortedTags"
515516
:key="'tag-' + tag"
517+
:href="tagHref(tag)"
516518
class="group cursor-pointer relative inline-flex items-center rounded-full overflow-hidden px-3 py-1 text-xs font-medium ring-1 ring-inset hover:dark:bg-green-900 hover:dark:ring-green-600/80"
517-
:class="score
518-
? 'text-emerald-800 dark:text-emerald-200 ring-emerald-600/20 dark:ring-emerald-400/30'
519+
:class="score
520+
? 'text-emerald-800 dark:text-emerald-200 ring-emerald-600/20 dark:ring-emerald-400/30'
519521
: 'bg-emerald-100 dark:bg-emerald-900/30 text-emerald-800 dark:text-emerald-200 ring-emerald-600/20 dark:ring-emerald-400/30'"
520-
:title="'Score: ' + (score ? Math.round(score * 100) + '%' : 'No score')">
522+
:title="'Search tag: ' + tag + (score ? ' · Score: ' + Math.round(score * 100) + '%' : '')">
521523
<!-- Background fill based on score -->
522524
<div v-if="score"
523525
class="group-hover:hidden absolute inset-0 bg-gradient-to-r from-emerald-300 to-emerald-400 dark:from-emerald-700 dark:to-emerald-800"
@@ -527,7 +529,7 @@ const App = {
527529
class="group-hover:hidden absolute inset-0 bg-emerald-100 dark:bg-emerald-900/30"></div>
528530
<!-- Text content -->
529531
<span class="relative z-10">{{ tag }}</span>
530-
</div>
532+
</a>
531533
</div>
532534
</div>
533535
</div>
@@ -671,6 +673,20 @@ const App = {
671673
return item
672674
})
673675

676+
// Tags as [tag, score] entries ordered by strength (highest score first)
677+
const sortedTags = computed(() =>
678+
Object.entries(parsedMedia.value?.tags ?? {})
679+
.sort((a, b) => (b[1] || 0) - (a[1] || 0)))
680+
681+
// Clicking a tag/category links to the gallery filtered by it (read by medias.mjs).
682+
// Audio media opens the Audio tab (#audio) so the filter lands on the matching grid.
683+
const galleryHref = (kind, value) => {
684+
const hash = (parsedMedia.value?.type || '').toLowerCase() === 'audio' ? '#audio' : ''
685+
return `/m?${kind}=${encodeURIComponent(value)}${hash}`
686+
}
687+
const tagHref = (tag) => galleryHref('tag', tag)
688+
const categoryHref = (category) => galleryHref('category', category)
689+
674690
const aspectRatio = computed(() => {
675691
const media = parsedMedia.value
676692
if (!media) return null
@@ -1029,6 +1045,9 @@ const App = {
10291045
media,
10301046
error,
10311047
parsedMedia,
1048+
sortedTags,
1049+
tagHref,
1050+
categoryHref,
10321051
aspectRatio,
10331052
resolveUrl,
10341053
formatDate,

0 commit comments

Comments
 (0)