= ({ children }) => {
);
};
+const AUDIO_ATTACHMENT_RE = /\.(wav|mp3|m4a|ogg|oga|flac|webm|mp4|aac)$/i;
+
+const isAudioAttachment = (name: string | undefined, contentType: string) =>
+ /^audio\//i.test(contentType) || AUDIO_ATTACHMENT_RE.test(name ?? "");
+
const AttachmentThumb: FC = () => {
const src = useAttachmentSrc();
const name = useAuiState(({ attachment }) => attachment.name);
+ const contentType = useAuiState(
+ ({ attachment }) =>
+ (attachment as { file?: File }).file?.type ??
+ (attachment as { contentType?: string }).contentType ??
+ "",
+ );
if (src) {
return (
@@ -137,7 +148,7 @@ const AttachmentThumb: FC = () => {
return (
@@ -159,7 +170,12 @@ const AttachmentUI: FC = () => {
case "document":
return "Document";
case "file":
- return "File";
+ return isAudioAttachment(
+ attachment.name,
+ (attachment as { file?: File }).file?.type ?? "",
+ )
+ ? "Audio"
+ : "File";
default:
throw new Error(`Unknown attachment type: ${type as string}`);
}
diff --git a/studio/frontend/src/components/assistant-ui/thread.tsx b/studio/frontend/src/components/assistant-ui/thread.tsx
index 11d91cc952..7f1527d326 100644
--- a/studio/frontend/src/components/assistant-ui/thread.tsx
+++ b/studio/frontend/src/components/assistant-ui/thread.tsx
@@ -2315,7 +2315,13 @@ const ReasoningToggle: FC<{ side?: "top" | "bottom" }> = ({
)}
{effectiveReasoningEffortLevels
- .filter((level) => level !== "none")
+ // 'none' is a real template level for models like Inkling
+ // (effort 0 = thinking off); show it as a pick unless the
+ // dedicated off item above already covers it.
+ .filter(
+ (level) =>
+ level !== "none" || !effectiveSupportsReasoningOff,
+ )
.map((level) => (