Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion application/single_app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
EXECUTOR_TYPE = 'thread'
EXECUTOR_MAX_WORKERS = 30
SESSION_TYPE = 'filesystem'
VERSION = "0.261.212"
VERSION = "0.261.213"
IS_DEVELOPMENT = is_development_env_enabled()

# Opt-out for deployments where App Service Easy Auth is active but the platform
Expand Down
24 changes: 19 additions & 5 deletions application/v2_ui/src/components/chat/AssistThread.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { useEffect, useId, useRef, useState, type ReactNode } from 'react';
import { clsx } from 'clsx';
import { FileText, FolderOpen, Loader2, PenLine, RotateCcw, Send, Tag as TagIcon, X } from 'lucide-react';
import { ComposerEditor } from './ComposerEditor';
import type { AssistExchange, AssistThreadController } from '../../lib/assistThread';
import { CANCELLED_MESSAGE, type AssistExchange, type AssistThreadController } from '../../lib/assistThread';

/** A `#` chip a turn was sent with, shown read-only in the thread. */
export interface AssistReferenceChip {
Expand Down Expand Up @@ -48,6 +48,11 @@ export interface AssistThreadProps {
logFooter?: ReactNode;
/** What the assistant said in an exchange the server does not store. */
renderReply?: (exchange: AssistExchange) => ReactNode;
/**
* Shown for a request the user cancelled, in place of the default note that the change may
* still be applied. Only for an editor whose server writes nothing, so a cancel is safe.
*/
cancelledMessage?: string;
sendLabel: ReactNode;
busyLabel?: ReactNode;
sendAriaLabel?: string;
Expand All @@ -67,6 +72,8 @@ export interface AssistThreadProps {
* the references with its request, and the server must authorise them for the acting user.
*/
allowContext?: boolean;
/** With `allowContext`, offer `#` documents only: no tags and no workspaces. Off by default. */
contextDocumentsOnly?: boolean;
counterHint?: string;
density?: 'compact' | 'comfortable';
className?: string;
Expand Down Expand Up @@ -125,6 +132,7 @@ export function AssistThread({
logHeader,
logFooter,
renderReply,
cancelledMessage,
sendLabel,
busyLabel,
sendAriaLabel,
Expand All @@ -135,6 +143,7 @@ export function AssistThread({
describedBy,
composerNote,
allowContext = false,
contextDocumentsOnly = false,
counterHint,
density = 'compact',
className,
Expand Down Expand Up @@ -208,15 +217,19 @@ export function AssistThread({
</button>
</p>
) : exchange.status === 'done' ? (
<p className="whitespace-pre-wrap break-words">
{renderReply ? renderReply(exchange) : exchange.reply}
</p>
renderReply ? (
<div className="break-words">{renderReply(exchange)}</div>
) : (
<p className="whitespace-pre-wrap break-words">{exchange.reply}</p>
)
) : (
<>
{exchange.status === 'failed' ? (
<p role="alert" className="whitespace-pre-wrap break-words text-danger">{exchange.error}</p>
) : (
<p className="whitespace-pre-wrap break-words text-text-3">{exchange.error}</p>
<p className="whitespace-pre-wrap break-words text-text-3">
{cancelledMessage && exchange.error === CANCELLED_MESSAGE ? cancelledMessage : exchange.error}
</p>
)}
<div className="mt-1.5 flex flex-wrap gap-1">
<button
Expand Down Expand Up @@ -313,6 +326,7 @@ export function AssistThread({
textareaRef={thread.inputRef}
restricted
allowContext={allowContext}
contextDocumentsOnly={contextDocumentsOnly}
describedBy={[describedBy, counterId, thread.overLimit ? limitId : null]
.filter(Boolean).join(' ')}
invalid={thread.overLimit}
Expand Down
9 changes: 7 additions & 2 deletions application/v2_ui/src/components/chat/ComposerEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@
restricted?: boolean;
/** Restricted mode only: also offer `#` documents and tags and the Add context control. */
allowContext?: boolean;
/** With `allowContext`, offer documents only: no tags. Off by default. */
contextDocumentsOnly?: boolean;
describedBy?: string;
invalid?: boolean;
labelClassName?: string;
Expand Down Expand Up @@ -186,6 +188,7 @@
onUploadComplete,
restricted = false,
allowContext = false,
contextDocumentsOnly = false,
describedBy,
invalid = false,
labelClassName = 'sr-only',
Expand Down Expand Up @@ -226,7 +229,8 @@
// A restricted composer's requests can use only documents and tags, so whole
// workspaces are not offered there.
workspacesEnabled: !restricted,
}), [bootstrap?.scope, features.enable_group_workspaces, features.enable_public_workspaces, restricted]);
documentsOnly: restricted && contextDocumentsOnly,
}), [bootstrap?.scope, contextDocumentsOnly, features.enable_group_workspaces, features.enable_public_workspaces, restricted]);

Check warning on line 233 in application/v2_ui/src/components/chat/ComposerEditor.tsx

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
const { candidates, loading } = useContextSuggestions(
disabled || !contextEnabled ? null : contextQuery?.query ?? null, scope,
);
Expand Down Expand Up @@ -829,7 +833,8 @@
});

return (
<div ref={holderRef} className="relative min-w-0" data-composer-editor={id}>
<div ref={holderRef} className="relative min-w-0" data-composer-editor={id}
data-composer-menu-open={menuOpen || undefined}>
{mention && !disabled && (
<MentionMenu suggestions={mentionSuggestions} activeIndex={mentionIndex} onSelect={applyMention}
placement={menuPlacement} />
Expand Down
5 changes: 4 additions & 1 deletion application/v2_ui/src/components/chat/ContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export interface ContextSearchScope {
* composer, whose requests can only use a document or a tag.
*/
workspacesEnabled?: boolean;
/** Offer documents only: no tags and no workspaces. For an editor whose requests hold documents. */
documentsOnly?: boolean;
}

/**
Expand Down Expand Up @@ -87,6 +89,7 @@ export function useContextSuggestions(
groupsEnabled: scopeRef.current.groupsEnabled,
publicEnabled: scopeRef.current.publicEnabled,
includeWorkspaces: scopeRef.current.workspacesEnabled !== false,
documentsOnly: scopeRef.current.documentsOnly === true,
signal: controller.signal,
})
.then((found) => {
Expand All @@ -110,7 +113,7 @@ export function useContextSuggestions(
window.clearTimeout(timer);
controller.abort();
};
}, [query, groupIds, publicIds, scope.groupsEnabled, scope.publicEnabled, scope.workspacesEnabled]);
}, [query, groupIds, publicIds, scope.documentsOnly, scope.groupsEnabled, scope.publicEnabled, scope.workspacesEnabled]);

return { candidates, loading };
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
const holder = useRef<HTMLDivElement>(null);
const searchRef = useRef<HTMLInputElement>(null);
const includeWorkspaces = scope.workspacesEnabled !== false;
const documentsOnly = imagesOnly || scope.documentsOnly === true;

useEffect(() => {
searchRef.current?.focus();
Expand Down Expand Up @@ -100,7 +101,7 @@
signal: controller.signal,
includeUnavailable: true,
documentFilter: imagesOnly ? isReferenceImageDocument : undefined,
documentsOnly: imagesOnly,
documentsOnly,
})
.then((found) => {
if (!controller.signal.aborted) {
Expand All @@ -122,7 +123,7 @@
};
// The scope arrays are rebuilt on every bootstrap read, so this keys on the flags and
// the query rather than on array identity.
}, [query, scope.groupsEnabled, scope.publicEnabled, includeWorkspaces, imagesOnly]);
}, [query, scope.groupsEnabled, scope.publicEnabled, includeWorkspaces, imagesOnly, documentsOnly]);

// Grouped by workspace, which is how the chip row groups them too: a reader scanning for
// "the contract in Marketing" is looking for the workspace first.
Expand Down Expand Up @@ -164,7 +165,7 @@
event.stopPropagation();
}
}}
placeholder={imagesOnly ? 'Search images…'
placeholder={imagesOnly ? 'Search images…' : documentsOnly ? 'Search documents…'

Check warning on line 168 in application/v2_ui/src/components/chat/DocumentPickerPopover.tsx

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.
: includeWorkspaces ? 'Search documents, tags and workspaces…' : 'Search documents and tags…'}
aria-label={imagesOnly ? 'Search images' : 'Search documents'}
className={clsx(
Expand Down
Loading
Loading