Skip to content

Commit 41addb5

Browse files
fix(ask_sb): Various improvements to the references system (#396)
1 parent efc9656 commit 41addb5

21 files changed

+966
-855
lines changed

packages/web/package.json

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@
1212
"stripe:listen": "stripe listen --forward-to http://localhost:3000/api/stripe"
1313
},
1414
"dependencies": {
15-
"@ai-sdk/amazon-bedrock": "3.0.0-beta.9",
16-
"@ai-sdk/anthropic": "2.0.0-beta.8",
17-
"@ai-sdk/azure": "2.0.0-beta.11",
18-
"@ai-sdk/deepseek": "1.0.0-beta.8",
19-
"@ai-sdk/google": "2.0.0-beta.14",
20-
"@ai-sdk/google-vertex": "3.0.0-beta.16",
21-
"@ai-sdk/mistral": "2.0.0-beta.6",
22-
"@ai-sdk/openai": "2.0.0-beta.11",
23-
"@ai-sdk/react": "2.0.0-beta.26",
24-
"@ai-sdk/xai": "2.0.0-beta.10",
15+
"@ai-sdk/amazon-bedrock": "3.0.0-beta.10",
16+
"@ai-sdk/anthropic": "2.0.0-beta.9",
17+
"@ai-sdk/azure": "2.0.0-beta.12",
18+
"@ai-sdk/deepseek": "1.0.0-beta.9",
19+
"@ai-sdk/google": "2.0.0-beta.15",
20+
"@ai-sdk/google-vertex": "3.0.0-beta.17",
21+
"@ai-sdk/mistral": "2.0.0-beta.7",
22+
"@ai-sdk/openai": "2.0.0-beta.12",
23+
"@ai-sdk/react": "2.0.0-beta.28",
24+
"@ai-sdk/xai": "2.0.0-beta.11",
2525
"@auth/prisma-adapter": "^2.7.4",
2626
"@codemirror/commands": "^6.6.0",
2727
"@codemirror/lang-cpp": "^6.0.2",
@@ -108,7 +108,7 @@
108108
"@vercel/otel": "^1.13.0",
109109
"@viz-js/lang-dot": "^1.0.4",
110110
"@xiechao/codemirror-lang-handlebars": "^1.0.4",
111-
"ai": "5.0.0-beta.26",
111+
"ai": "5.0.0-beta.28",
112112
"ajv": "^8.17.1",
113113
"bcryptjs": "^3.0.2",
114114
"class-variance-authority": "^0.7.0",

packages/web/src/app/[domain]/browse/[...path]/components/pureCodePreviewPanel.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,9 @@ export const PureCodePreviewPanel = ({
136136
}, [editorRef, highlightRange]);
137137

138138
const onFindReferences = useCallback((symbolName: string) => {
139-
captureEvent('wa_browse_find_references_pressed', {});
139+
captureEvent('wa_find_references_pressed', {
140+
source: 'browse',
141+
});
140142
createAuditAction({
141143
action: "user.performed_find_references",
142144
metadata: {
@@ -160,7 +162,9 @@ export const PureCodePreviewPanel = ({
160162
// If we resolve multiple matches, instead of navigating to the first match, we should
161163
// instead popup the bottom sheet with the list of matches.
162164
const onGotoDefinition = useCallback((symbolName: string, symbolDefinitions: SymbolDefinition[]) => {
163-
captureEvent('wa_browse_goto_definition_pressed', {});
165+
captureEvent('wa_goto_definition_pressed', {
166+
source: 'browse',
167+
});
164168
createAuditAction({
165169
action: "user.performed_goto_definition",
166170
metadata: {

packages/web/src/app/[domain]/search/components/codePreviewPanel/codePreview.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,9 @@ export const CodePreview = ({
119119
}, [onSelectedMatchIndexChange]);
120120

121121
const onGotoDefinition = useCallback((symbolName: string, symbolDefinitions: SymbolDefinition[]) => {
122-
captureEvent('wa_preview_panel_goto_definition_pressed', {});
122+
captureEvent('wa_goto_definition_pressed', {
123+
source: 'preview',
124+
});
123125
createAuditAction({
124126
action: "user.performed_goto_definition",
125127
metadata: {
@@ -163,7 +165,9 @@ export const CodePreview = ({
163165
}, [captureEvent, file.filepath, file.language, file.revision, navigateToPath, repoName, domain]);
164166

165167
const onFindReferences = useCallback((symbolName: string) => {
166-
captureEvent('wa_preview_panel_find_references_pressed', {});
168+
captureEvent('wa_find_references_pressed', {
169+
source: 'preview',
170+
});
167171
createAuditAction({
168172
action: "user.performed_find_references",
169173
metadata: {

packages/web/src/features/chat/components/chatThread/chatThread.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ export const ChatThread = ({
269269
return (
270270
<Fragment key={index}>
271271
<ChatThreadListItem
272+
index={index}
272273
chatId={chatId}
273274
userMessage={userMessage}
274275
assistantMessage={assistantMessage}

packages/web/src/features/chat/components/chatThread/chatThreadListItem.tsx

Lines changed: 118 additions & 176 deletions
Large diffs are not rendered by default.

packages/web/src/features/chat/components/chatThread/codeFoldingExtension.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,8 @@ describe('StateField Integration', () => {
482482
path: 'test.ts',
483483
id: '1',
484484
type: 'file',
485-
range: { startLine: 10, endLine: 15 }
485+
range: { startLine: 10, endLine: 15 },
486+
repo: 'github.com/sourcebot-dev/sourcebot'
486487
}
487488
];
488489

packages/web/src/features/chat/components/chatThread/codeFoldingExtension.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,9 @@ const createDecorations = (state: EditorState, foldingState: FoldingState): Deco
342342
decorations.push(decoration.range(from, to));
343343
});
344344

345+
// Sort decorations by their 'from' position to ensure proper ordering
346+
decorations.sort((a, b) => a.from - b.from);
347+
345348
return Decoration.set(decorations);
346349
};
347350

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
'use client';
2+
3+
import { Card, CardContent } from '@/components/ui/card';
4+
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
5+
import { Separator } from '@/components/ui/separator';
6+
import { Skeleton } from '@/components/ui/skeleton';
7+
import { cn } from '@/lib/utils';
8+
import { Brain, ChevronDown, ChevronRight, Clock, Cpu, InfoIcon, Loader2, Zap } from 'lucide-react';
9+
import { MarkdownRenderer } from './markdownRenderer';
10+
import { FindSymbolDefinitionsToolComponent } from './tools/findSymbolDefinitionsToolComponent';
11+
import { FindSymbolReferencesToolComponent } from './tools/findSymbolReferencesToolComponent';
12+
import { ReadFilesToolComponent } from './tools/readFilesToolComponent';
13+
import { SearchCodeToolComponent } from './tools/searchCodeToolComponent';
14+
import { SBChatMessageMetadata, SBChatMessagePart } from '../../types';
15+
16+
17+
interface DetailsCardProps {
18+
isExpanded: boolean;
19+
onExpandedChanged: (isExpanded: boolean) => void;
20+
isThinking: boolean;
21+
isStreaming: boolean;
22+
thinkingSteps: SBChatMessagePart[][];
23+
metadata?: SBChatMessageMetadata;
24+
}
25+
26+
export const DetailsCard = ({
27+
isExpanded,
28+
onExpandedChanged,
29+
isThinking,
30+
isStreaming,
31+
metadata,
32+
thinkingSteps,
33+
}: DetailsCardProps) => {
34+
35+
return (
36+
<Card className="mb-4">
37+
<Collapsible open={isExpanded} onOpenChange={onExpandedChanged}>
38+
<CollapsibleTrigger asChild>
39+
<CardContent
40+
className={cn("p-3 cursor-pointer hover:bg-muted", {
41+
"rounded-lg": !isExpanded,
42+
"rounded-t-lg": isExpanded,
43+
})}
44+
>
45+
<div className="flex items-center justify-between w-full">
46+
<div className="flex items-center space-x-4">
47+
48+
<p className="flex items-center font-semibold text-muted-foreground text-sm">
49+
{isThinking ? (
50+
<>
51+
<Loader2 className="w-4 h-4 animate-spin mr-1 flex-shrink-0" />
52+
Thinking...
53+
</>
54+
) : (
55+
<>
56+
<InfoIcon className="w-4 h-4 mr-1 flex-shrink-0" />
57+
Details
58+
</>
59+
)}
60+
</p>
61+
{!isStreaming && (
62+
<>
63+
<Separator orientation="vertical" className="h-4" />
64+
{metadata?.modelName && (
65+
<div className="flex items-center text-xs">
66+
<Cpu className="w-3 h-3 mr-1 flex-shrink-0" />
67+
{metadata?.modelName}
68+
</div>
69+
)}
70+
{metadata?.totalTokens && (
71+
<div className="flex items-center text-xs">
72+
<Zap className="w-3 h-3 mr-1 flex-shrink-0" />
73+
{metadata?.totalTokens} tokens
74+
</div>
75+
)}
76+
{metadata?.totalResponseTimeMs && (
77+
<div className="flex items-center text-xs">
78+
<Clock className="w-3 h-3 mr-1 flex-shrink-0" />
79+
{metadata?.totalResponseTimeMs / 1000} seconds
80+
</div>
81+
)}
82+
<div className="flex items-center text-xs">
83+
<Brain className="w-3 h-3 mr-1 flex-shrink-0" />
84+
{`${thinkingSteps.length} step${thinkingSteps.length === 1 ? '' : 's'}`}
85+
</div>
86+
</>
87+
)}
88+
</div>
89+
90+
{isExpanded ? (
91+
<ChevronDown className="w-4 h-4 text-muted-foreground" />
92+
) : (
93+
<ChevronRight className="w-4 h-4 text-muted-foreground" />
94+
)}
95+
</div>
96+
</CardContent>
97+
</CollapsibleTrigger>
98+
<CollapsibleContent>
99+
<CardContent className="mt-2 space-y-6">
100+
{thinkingSteps.length === 0 ? (
101+
isStreaming ? (
102+
<Skeleton className="h-24 w-full" />
103+
) : (
104+
<p className="text-sm text-muted-foreground">No thinking steps</p>
105+
)
106+
) : thinkingSteps.map((step, index) => {
107+
return (
108+
<div
109+
key={index}
110+
className="border-l-2 pl-4 relative border-muted"
111+
>
112+
<div
113+
className={`absolute left-[-9px] top-1 w-4 h-4 rounded-full flex items-center justify-center bg-muted`}
114+
>
115+
<span
116+
className={`text-xs font-semibold`}
117+
>
118+
{index + 1}
119+
</span>
120+
</div>
121+
{step.map((part, index) => {
122+
switch (part.type) {
123+
case 'reasoning':
124+
case 'text':
125+
return (
126+
<MarkdownRenderer
127+
key={index}
128+
content={part.text}
129+
className="text-sm"
130+
/>
131+
)
132+
case 'tool-readFiles':
133+
return (
134+
<ReadFilesToolComponent
135+
key={index}
136+
part={part}
137+
/>
138+
)
139+
case 'tool-searchCode':
140+
return (
141+
<SearchCodeToolComponent
142+
key={index}
143+
part={part}
144+
/>
145+
)
146+
case 'tool-findSymbolDefinitions':
147+
return (
148+
<FindSymbolDefinitionsToolComponent
149+
key={index}
150+
part={part}
151+
/>
152+
)
153+
case 'tool-findSymbolReferences':
154+
return (
155+
<FindSymbolReferencesToolComponent
156+
key={index}
157+
part={part}
158+
/>
159+
)
160+
default:
161+
return null;
162+
}
163+
})}
164+
</div>
165+
)
166+
})}
167+
</CardContent>
168+
</CollapsibleContent>
169+
</Collapsible>
170+
</Card>
171+
)
172+
}

packages/web/src/features/chat/components/chatThread/markdownRenderer.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,12 @@ function remarkReferencesPlugin() {
6363
return {
6464
type: 'html',
6565
// @note: if you add additional attributes to this span, make sure to update the rehypeSanitize plugin to allow them.
66+
//
67+
// @note: we attach the reference id to the DOM element as a class name since there may be multiple reference elements
68+
// with the same id (i.e., referencing the same file & range).
6669
value: `<span
6770
role="button"
68-
id="${fileReference.id}"
71+
class="${fileReference.id}"
6972
className="font-mono cursor-pointer text-xs border px-1 py-[1.5px] rounded-md transition-all duration-150 bg-chat-reference"
7073
title="Click to navigate to code"
7174
${REFERENCE_PAYLOAD_ATTRIBUTE}="${encodeURIComponent(JSON.stringify(fileReference))}"

0 commit comments

Comments
 (0)