@alpaca-editor/core 1.0.4027 → 1.0.4030
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ActionButton.js +2 -2
- package/dist/components/ActionButton.js.map +1 -1
- package/dist/components/SimpleLanguageSelector.js +3 -1
- package/dist/components/SimpleLanguageSelector.js.map +1 -1
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/config/config.js +1 -1
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +1 -1
- package/dist/editor/ContextMenu.js +0 -1
- package/dist/editor/ContextMenu.js.map +1 -1
- package/dist/editor/Editor.js +9 -3
- package/dist/editor/Editor.js.map +1 -1
- package/dist/editor/FieldListField.js +16 -24
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/ImageEditButton.js +1 -1
- package/dist/editor/ImageEditButton.js.map +1 -1
- package/dist/editor/ImageEditor.js +1 -1
- package/dist/editor/ImageEditor.js.map +1 -1
- package/dist/editor/MainLayout.js +2 -2
- package/dist/editor/MainLayout.js.map +1 -1
- package/dist/editor/Terminal.js +1 -1
- package/dist/editor/Terminal.js.map +1 -1
- package/dist/editor/Titlebar.js +0 -1
- package/dist/editor/Titlebar.js.map +1 -1
- package/dist/editor/ai/AgentCostDisplay.d.ts +26 -0
- package/dist/editor/ai/AgentCostDisplay.js +65 -0
- package/dist/editor/ai/AgentCostDisplay.js.map +1 -0
- package/dist/editor/ai/Agents.js +59 -15
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiPromptPopover.d.ts +7 -0
- package/dist/editor/ai/AiPromptPopover.js +111 -0
- package/dist/editor/ai/AiPromptPopover.js.map +1 -0
- package/dist/editor/ai/AiResponseMessage.d.ts +1 -0
- package/dist/editor/ai/AiResponseMessage.js +101 -23
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/AiTerminal.d.ts +15 -1
- package/dist/editor/ai/AiTerminal.js +379 -48
- package/dist/editor/ai/AiTerminal.js.map +1 -1
- package/dist/editor/ai/editorAiContext.d.ts +0 -1
- package/dist/editor/ai/editorAiContext.js +0 -2
- package/dist/editor/ai/editorAiContext.js.map +1 -1
- package/dist/editor/client/EditorClient.d.ts +3 -2
- package/dist/editor/client/EditorClient.js +326 -68
- package/dist/editor/client/EditorClient.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +6 -4
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/client/fieldModificationStore.d.ts +19 -0
- package/dist/editor/client/fieldModificationStore.js +125 -0
- package/dist/editor/client/fieldModificationStore.js.map +1 -0
- package/dist/editor/client/itemsRepository.d.ts +1 -1
- package/dist/editor/client/itemsRepository.js +38 -28
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/client/operations.d.ts +1 -0
- package/dist/editor/client/operations.js +39 -31
- package/dist/editor/client/operations.js.map +1 -1
- package/dist/editor/commands/componentCommands.js +5 -3
- package/dist/editor/commands/componentCommands.js.map +1 -1
- package/dist/editor/commands/itemCommands.js.map +1 -1
- package/dist/editor/component-designer/aiContext.js +0 -2
- package/dist/editor/component-designer/aiContext.js.map +1 -1
- package/dist/editor/field-types/DropLinkEditor.js +1 -1
- package/dist/editor/field-types/DropLinkEditor.js.map +1 -1
- package/dist/editor/field-types/MultiLineText.js +5 -7
- package/dist/editor/field-types/MultiLineText.js.map +1 -1
- package/dist/editor/field-types/RichTextEditorComponent.js +5 -7
- package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
- package/dist/editor/field-types/SingleLineText.js +5 -7
- package/dist/editor/field-types/SingleLineText.js.map +1 -1
- package/dist/editor/hooks/useEditorSettings.d.ts +17 -0
- package/dist/editor/hooks/useEditorSettings.js +61 -0
- package/dist/editor/hooks/useEditorSettings.js.map +1 -0
- package/dist/editor/menubar/ItemActionsMenu.js +2 -2
- package/dist/editor/menubar/ItemActionsMenu.js.map +1 -1
- package/dist/editor/menubar/PageSelector.js +1 -1
- package/dist/editor/menubar/PageSelector.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/EditControls.js +1 -1
- package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/InsertControls.js +1 -1
- package/dist/editor/menubar/toolbar-sections/InsertControls.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js +1 -1
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/ViewportControls.js +1 -1
- package/dist/editor/menubar/toolbar-sections/ViewportControls.js.map +1 -1
- package/dist/editor/page-editor-chrome/FieldEditedIndicators.js +4 -3
- package/dist/editor/page-editor-chrome/FieldEditedIndicators.js.map +1 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js +9 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js +0 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
- package/dist/editor/page-viewer/EditorForm.js +1 -1
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/PageViewer.js +9 -8
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +7 -1
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/page-viewer/pageViewContext.js +40 -6
- package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
- package/dist/editor/reviews/Comment.js +7 -6
- package/dist/editor/reviews/Comment.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +84 -12
- package/dist/editor/services/agentService.js +256 -15
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +17 -3
- package/dist/editor/services/aiService.js +5 -3
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/services/contextService.js +0 -1
- package/dist/editor/services/contextService.js.map +1 -1
- package/dist/editor/services/systemService.d.ts +2 -1
- package/dist/editor/services/systemService.js +3 -0
- package/dist/editor/services/systemService.js.map +1 -1
- package/dist/editor/sidebar/ComponentPalette.js +1 -1
- package/dist/editor/sidebar/ComponentPalette.js.map +1 -1
- package/dist/editor/sidebar/EditHistory.js +2 -2
- package/dist/editor/sidebar/EditHistory.js.map +1 -1
- package/dist/editor/sidebar/GraphQL.d.ts +1 -0
- package/dist/editor/sidebar/GraphQL.js +8 -2
- package/dist/editor/sidebar/GraphQL.js.map +1 -1
- package/dist/editor/sidebar/MainContentTree.js +1 -1
- package/dist/editor/sidebar/MainContentTree.js.map +1 -1
- package/dist/editor/sidebar/SEOInfo.js +1 -1
- package/dist/editor/sidebar/SEOInfo.js.map +1 -1
- package/dist/editor/sidebar/ViewSelector.d.ts +4 -1
- package/dist/editor/sidebar/ViewSelector.js +64 -48
- package/dist/editor/sidebar/ViewSelector.js.map +1 -1
- package/dist/editor/ui/PerfectTree.js +2 -11
- package/dist/editor/ui/PerfectTree.js.map +1 -1
- package/dist/editor/ui/SimpleIconButton.d.ts +2 -0
- package/dist/editor/ui/SimpleIconButton.js +8 -4
- package/dist/editor/ui/SimpleIconButton.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/page-wizard/steps/CollectStep.js +1 -1
- package/dist/page-wizard/steps/CollectStep.js.map +1 -1
- package/dist/page-wizard/steps/StructureStep.js +1 -1
- package/dist/page-wizard/steps/StructureStep.js.map +1 -1
- package/dist/page-wizard/steps/TranslateStep.js +233 -18
- package/dist/page-wizard/steps/TranslateStep.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/splash-screen/RecentPages.js +1 -13
- package/dist/splash-screen/RecentPages.js.map +1 -1
- package/dist/splash-screen/SplashScreen.js +1 -1
- package/dist/splash-screen/SplashScreen.js.map +1 -1
- package/dist/styles.css +88 -3
- package/dist/types.d.ts +6 -0
- package/package.json +2 -2
- package/src/components/ActionButton.tsx +3 -2
- package/src/components/SimpleLanguageSelector.tsx +6 -1
- package/src/config/config.tsx +1 -1
- package/src/config/types.ts +1 -1
- package/src/editor/ContextMenu.tsx +0 -3
- package/src/editor/Editor.tsx +11 -3
- package/src/editor/FieldListField.tsx +22 -31
- package/src/editor/ImageEditButton.tsx +1 -0
- package/src/editor/ImageEditor.tsx +1 -0
- package/src/editor/MainLayout.tsx +2 -2
- package/src/editor/Terminal.tsx +1 -1
- package/src/editor/Titlebar.tsx +0 -2
- package/src/editor/ai/AgentCostDisplay.tsx +237 -0
- package/src/editor/ai/Agents.tsx +69 -20
- package/src/editor/ai/AiPromptPopover.tsx +209 -0
- package/src/editor/ai/AiResponseMessage.tsx +201 -60
- package/src/editor/ai/AiTerminal.tsx +502 -71
- package/src/editor/ai/editorAiContext.ts +0 -3
- package/src/editor/client/EditorClient.tsx +409 -117
- package/src/editor/client/editContext.ts +7 -5
- package/src/editor/client/fieldModificationStore.ts +196 -0
- package/src/editor/client/itemsRepository.ts +41 -31
- package/src/editor/client/operations.ts +95 -76
- package/src/editor/commands/componentCommands.tsx +9 -3
- package/src/editor/commands/itemCommands.tsx +0 -1
- package/src/editor/component-designer/aiContext.ts +0 -2
- package/src/editor/field-types/DropLinkEditor.tsx +1 -1
- package/src/editor/field-types/MultiLineText.tsx +9 -9
- package/src/editor/field-types/RichTextEditorComponent.tsx +8 -8
- package/src/editor/field-types/SingleLineText.tsx +9 -9
- package/src/editor/hooks/useEditorSettings.ts +68 -0
- package/src/editor/menubar/ItemActionsMenu.tsx +3 -2
- package/src/editor/menubar/PageSelector.tsx +1 -1
- package/src/editor/menubar/toolbar-sections/EditControls.tsx +1 -0
- package/src/editor/menubar/toolbar-sections/InsertControls.tsx +1 -0
- package/src/editor/menubar/toolbar-sections/UtilityControls.tsx +2 -0
- package/src/editor/menubar/toolbar-sections/ViewportControls.tsx +2 -0
- package/src/editor/page-editor-chrome/FieldEditedIndicators.tsx +4 -3
- package/src/editor/page-editor-chrome/FrameMenu.tsx +10 -1
- package/src/editor/page-editor-chrome/useInlineAICompletion.tsx +0 -1
- package/src/editor/page-viewer/EditorForm.tsx +1 -0
- package/src/editor/page-viewer/PageViewer.tsx +9 -8
- package/src/editor/page-viewer/PageViewerFrame.tsx +7 -1
- package/src/editor/page-viewer/pageViewContext.ts +40 -5
- package/src/editor/reviews/Comment.tsx +7 -7
- package/src/editor/services/agentService.ts +405 -31
- package/src/editor/services/aiService.ts +22 -5
- package/src/editor/services/contextService.ts +0 -1
- package/src/editor/services/systemService.ts +7 -1
- package/src/editor/sidebar/ComponentPalette.tsx +4 -1
- package/src/editor/sidebar/EditHistory.tsx +2 -0
- package/src/editor/sidebar/GraphQL.tsx +19 -7
- package/src/editor/sidebar/MainContentTree.tsx +1 -1
- package/src/editor/sidebar/SEOInfo.tsx +1 -1
- package/src/editor/sidebar/ViewSelector.tsx +80 -64
- package/src/editor/ui/PerfectTree.tsx +2 -18
- package/src/editor/ui/SimpleIconButton.tsx +56 -38
- package/src/index.ts +2 -0
- package/src/page-wizard/steps/CollectStep.tsx +0 -2
- package/src/page-wizard/steps/StructureStep.tsx +3 -0
- package/src/page-wizard/steps/TranslateStep.tsx +473 -62
- package/src/revision.ts +2 -2
- package/src/splash-screen/RecentPages.tsx +0 -14
- package/src/splash-screen/SplashScreen.tsx +3 -2
- package/src/types.ts +7 -0
- package/dist/editor/ai/AiPopup.d.ts +0 -10
- package/dist/editor/ai/AiPopup.js +0 -23
- package/dist/editor/ai/AiPopup.js.map +0 -1
- package/dist/editor/ai/AiToolCall.d.ts +0 -5
- package/dist/editor/ai/AiToolCall.js +0 -28
- package/dist/editor/ai/AiToolCall.js.map +0 -1
- package/src/editor/ai/AiPopup.tsx +0 -59
- package/src/editor/ai/AiToolCall.tsx +0 -71
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
4
|
+
import {
|
|
5
|
+
Popover,
|
|
6
|
+
PopoverContent,
|
|
7
|
+
PopoverTrigger,
|
|
8
|
+
} from "../../components/ui/popover";
|
|
9
|
+
import { useEditorSettings } from "../hooks/useEditorSettings";
|
|
10
|
+
|
|
11
|
+
import { ExecutePromptResponse } from "../services/aiService";
|
|
12
|
+
|
|
13
|
+
interface AgentCostDisplayProps {
|
|
14
|
+
/** Optional className for styling */
|
|
15
|
+
className?: string;
|
|
16
|
+
/** Current response data with token usage */
|
|
17
|
+
response?: {
|
|
18
|
+
numInputTokens: number;
|
|
19
|
+
numOutputTokens: number;
|
|
20
|
+
numCachedTokens: number;
|
|
21
|
+
totalCost?: number;
|
|
22
|
+
inputCost?: number;
|
|
23
|
+
outputCost?: number;
|
|
24
|
+
cachedCost?: number;
|
|
25
|
+
} | null;
|
|
26
|
+
/** Cumulative token usage across all messages in the session */
|
|
27
|
+
totalTokens?: {
|
|
28
|
+
input: number;
|
|
29
|
+
output: number;
|
|
30
|
+
cached: number;
|
|
31
|
+
totalCost: number;
|
|
32
|
+
inputCost: number;
|
|
33
|
+
outputCost: number;
|
|
34
|
+
cachedCost: number;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function AgentCostDisplay({
|
|
39
|
+
className = "",
|
|
40
|
+
response,
|
|
41
|
+
totalTokens,
|
|
42
|
+
}: AgentCostDisplayProps) {
|
|
43
|
+
const [showCostPopover, setShowCostPopover] = useState(false);
|
|
44
|
+
const costPopoverRef = useRef<HTMLDivElement>(null);
|
|
45
|
+
const editorSettings = useEditorSettings();
|
|
46
|
+
const currency = editorSettings?.currency || "USD";
|
|
47
|
+
|
|
48
|
+
// Handle click outside for cost popover
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
function handleClickOutside(event: MouseEvent) {
|
|
51
|
+
if (
|
|
52
|
+
costPopoverRef.current &&
|
|
53
|
+
!costPopoverRef.current.contains(event.target as Node)
|
|
54
|
+
) {
|
|
55
|
+
setShowCostPopover(false);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (showCostPopover) {
|
|
60
|
+
document.addEventListener("click", handleClickOutside);
|
|
61
|
+
return () => {
|
|
62
|
+
document.removeEventListener("click", handleClickOutside);
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}, [showCostPopover]);
|
|
66
|
+
|
|
67
|
+
// Calculate estimated cost based on typical pricing (rough estimates)
|
|
68
|
+
// Claude 3.5 Sonnet pricing: ~$3/MTok input, ~$15/MTok output
|
|
69
|
+
const calculateEstimatedCost = (
|
|
70
|
+
inputTokens: number,
|
|
71
|
+
outputTokens: number,
|
|
72
|
+
cachedTokens: number,
|
|
73
|
+
) => {
|
|
74
|
+
const inputCost = (inputTokens / 1000000) * 3; // $3 per million tokens
|
|
75
|
+
const outputCost = (outputTokens / 1000000) * 15; // $15 per million tokens
|
|
76
|
+
const cachedCost = (cachedTokens / 1000000) * 0.3; // Cached tokens are typically much cheaper
|
|
77
|
+
return {
|
|
78
|
+
inputCost,
|
|
79
|
+
outputCost,
|
|
80
|
+
cachedCost,
|
|
81
|
+
totalCost: inputCost + outputCost + cachedCost,
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Use actual cost data if available, otherwise calculate estimates
|
|
86
|
+
const currentCost = response
|
|
87
|
+
? response.totalCost !== undefined
|
|
88
|
+
? {
|
|
89
|
+
inputCost: response.inputCost ?? 0,
|
|
90
|
+
outputCost: response.outputCost ?? 0,
|
|
91
|
+
cachedCost: response.cachedCost ?? 0,
|
|
92
|
+
totalCost: response.totalCost,
|
|
93
|
+
}
|
|
94
|
+
: calculateEstimatedCost(
|
|
95
|
+
response.numInputTokens,
|
|
96
|
+
response.numOutputTokens,
|
|
97
|
+
response.numCachedTokens,
|
|
98
|
+
)
|
|
99
|
+
: null;
|
|
100
|
+
|
|
101
|
+
const displayTotalCost =
|
|
102
|
+
totalTokens?.totalCost ?? currentCost?.totalCost ?? 0;
|
|
103
|
+
const hasTokenData =
|
|
104
|
+
response &&
|
|
105
|
+
(response.numInputTokens > 0 ||
|
|
106
|
+
response.numOutputTokens > 0 ||
|
|
107
|
+
response.numCachedTokens > 0);
|
|
108
|
+
const hasTotalData =
|
|
109
|
+
totalTokens &&
|
|
110
|
+
(totalTokens.input > 0 || totalTokens.output > 0 || totalTokens.cached > 0);
|
|
111
|
+
const hasCostData =
|
|
112
|
+
(response && response.totalCost && response.totalCost > 0) ||
|
|
113
|
+
(totalTokens && totalTokens.totalCost && totalTokens.totalCost > 0);
|
|
114
|
+
|
|
115
|
+
if (!hasTokenData && !hasTotalData && !hasCostData) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<div className={`relative ${className}`} ref={costPopoverRef}>
|
|
121
|
+
<Popover open={showCostPopover} onOpenChange={setShowCostPopover}>
|
|
122
|
+
<PopoverTrigger asChild>
|
|
123
|
+
<div
|
|
124
|
+
className="cursor-pointer text-right text-gray-400 hover:text-gray-600"
|
|
125
|
+
style={{ fontSize: "10px" }}
|
|
126
|
+
onClick={() => setShowCostPopover(!showCostPopover)}
|
|
127
|
+
>
|
|
128
|
+
Total Cost: {displayTotalCost.toFixed(2)} {currency}
|
|
129
|
+
</div>
|
|
130
|
+
</PopoverTrigger>
|
|
131
|
+
<PopoverContent className="w-80 p-4" align="end" side="bottom">
|
|
132
|
+
<div className="space-y-4">
|
|
133
|
+
<div className="border-b pb-2">
|
|
134
|
+
<h4 className="text-sm font-semibold">Cost Breakdown</h4>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<div className="space-y-3">
|
|
138
|
+
{/* Session Cost Breakdown */}
|
|
139
|
+
{(hasTotalData || hasTokenData) && (
|
|
140
|
+
<>
|
|
141
|
+
<div className="flex items-center justify-between">
|
|
142
|
+
<span className="text-sm font-semibold text-gray-600">
|
|
143
|
+
Session Cost:
|
|
144
|
+
</span>
|
|
145
|
+
<span className="text-sm font-semibold">
|
|
146
|
+
{displayTotalCost.toFixed(2)} {currency}
|
|
147
|
+
</span>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
<div className="space-y-2 border-t pt-2">
|
|
151
|
+
{hasTotalData ? (
|
|
152
|
+
<>
|
|
153
|
+
<div className="flex items-center justify-between">
|
|
154
|
+
<span className="text-xs text-gray-500">
|
|
155
|
+
Input tokens:
|
|
156
|
+
</span>
|
|
157
|
+
<span className="text-xs">
|
|
158
|
+
{totalTokens.input.toLocaleString()} (
|
|
159
|
+
{totalTokens.inputCost.toFixed(2)} {currency})
|
|
160
|
+
</span>
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
<div className="flex items-center justify-between">
|
|
164
|
+
<span className="text-xs text-gray-500">
|
|
165
|
+
Output tokens:
|
|
166
|
+
</span>
|
|
167
|
+
<span className="text-xs">
|
|
168
|
+
{totalTokens.output.toLocaleString()} (
|
|
169
|
+
{totalTokens.outputCost.toFixed(2)} {currency})
|
|
170
|
+
</span>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
{totalTokens.cached > 0 && (
|
|
174
|
+
<div className="flex items-center justify-between">
|
|
175
|
+
<span className="text-xs text-gray-500">
|
|
176
|
+
Cached tokens:
|
|
177
|
+
</span>
|
|
178
|
+
<span className="text-xs">
|
|
179
|
+
{totalTokens.cached.toLocaleString()} (
|
|
180
|
+
{totalTokens.cachedCost.toFixed(2)} {currency})
|
|
181
|
+
</span>
|
|
182
|
+
</div>
|
|
183
|
+
)}
|
|
184
|
+
</>
|
|
185
|
+
) : (
|
|
186
|
+
response &&
|
|
187
|
+
currentCost && (
|
|
188
|
+
<>
|
|
189
|
+
<div className="flex items-center justify-between">
|
|
190
|
+
<span className="text-xs text-gray-500">
|
|
191
|
+
Input tokens:
|
|
192
|
+
</span>
|
|
193
|
+
<span className="text-xs">
|
|
194
|
+
{response.numInputTokens.toLocaleString()}(
|
|
195
|
+
{currentCost.inputCost.toFixed(2)} {currency})
|
|
196
|
+
</span>
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
<div className="flex items-center justify-between">
|
|
200
|
+
<span className="text-xs text-gray-500">
|
|
201
|
+
Output tokens:
|
|
202
|
+
</span>
|
|
203
|
+
<span className="text-xs">
|
|
204
|
+
{response.numOutputTokens.toLocaleString()}(
|
|
205
|
+
{currentCost.outputCost.toFixed(2)} {currency})
|
|
206
|
+
</span>
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
{response.numCachedTokens > 0 && (
|
|
210
|
+
<div className="flex items-center justify-between">
|
|
211
|
+
<span className="text-xs text-gray-500">
|
|
212
|
+
Cached tokens:
|
|
213
|
+
</span>
|
|
214
|
+
<span className="text-xs">
|
|
215
|
+
{response.numCachedTokens.toLocaleString()}(
|
|
216
|
+
{currentCost.cachedCost.toFixed(2)} {currency})
|
|
217
|
+
</span>
|
|
218
|
+
</div>
|
|
219
|
+
)}
|
|
220
|
+
|
|
221
|
+
<div className="mt-2 text-xs text-gray-400">
|
|
222
|
+
* Estimated costs based on typical Claude 3.5
|
|
223
|
+
pricing
|
|
224
|
+
</div>
|
|
225
|
+
</>
|
|
226
|
+
)
|
|
227
|
+
)}
|
|
228
|
+
</div>
|
|
229
|
+
</>
|
|
230
|
+
)}
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
</PopoverContent>
|
|
234
|
+
</Popover>
|
|
235
|
+
</div>
|
|
236
|
+
);
|
|
237
|
+
}
|
package/src/editor/ai/Agents.tsx
CHANGED
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
PopoverTrigger,
|
|
19
19
|
} from "../../components/ui/popover";
|
|
20
20
|
import { Button } from "../../components/ui/button";
|
|
21
|
+
import { useEditContext } from "../client/editContext";
|
|
22
|
+
import { AiContext } from "./AiTerminal";
|
|
21
23
|
|
|
22
24
|
interface TerminalInstance {
|
|
23
25
|
id: string;
|
|
@@ -39,12 +41,13 @@ function convertAgentMessagesToTerminalMessages(
|
|
|
39
41
|
name: msg.name,
|
|
40
42
|
role: msg.role,
|
|
41
43
|
tool_calls:
|
|
42
|
-
msg.toolCalls?.map((toolCall) => ({
|
|
44
|
+
msg.toolCalls?.map((toolCall: any) => ({
|
|
43
45
|
id: toolCall.toolCallId,
|
|
44
46
|
displayName: toolCall.functionName,
|
|
45
47
|
function: {
|
|
46
48
|
name: toolCall.functionName,
|
|
47
49
|
arguments: toolCall.functionArguments || "",
|
|
50
|
+
result: toolCall.functionResult,
|
|
48
51
|
},
|
|
49
52
|
})) || [],
|
|
50
53
|
tool_call_id: msg.toolCallId,
|
|
@@ -77,6 +80,16 @@ export function Agents({
|
|
|
77
80
|
const [historyPopoverOpen, setHistoryPopoverOpen] = useState(false);
|
|
78
81
|
const [loadingAgents, setLoadingAgents] = useState(true);
|
|
79
82
|
const nextTerminalNumber = useRef(1);
|
|
83
|
+
const editContext = useEditContext();
|
|
84
|
+
|
|
85
|
+
// Create AI context for service calls
|
|
86
|
+
const createAgentContext = (): AiContext => ({
|
|
87
|
+
promptData: {
|
|
88
|
+
itemid: editContext?.currentItemDescriptor?.id,
|
|
89
|
+
language: editContext?.currentItemDescriptor?.language || "en",
|
|
90
|
+
version: editContext?.currentItemDescriptor?.version || 1,
|
|
91
|
+
},
|
|
92
|
+
});
|
|
80
93
|
|
|
81
94
|
// Load agents from backend on mount
|
|
82
95
|
useEffect(() => {
|
|
@@ -86,27 +99,43 @@ export function Agents({
|
|
|
86
99
|
const loadAgentsFromBackend = async () => {
|
|
87
100
|
try {
|
|
88
101
|
setLoadingAgents(true);
|
|
102
|
+
const context = createAgentContext();
|
|
89
103
|
|
|
90
104
|
// Load active agents
|
|
91
|
-
const activeAgentsResult = await getAgents("active");
|
|
105
|
+
const activeAgentsResult = await getAgents(context, "active");
|
|
92
106
|
|
|
93
107
|
// Load inactive agents for history
|
|
94
|
-
const inactiveAgentsResult = await getChatHistory(
|
|
108
|
+
const inactiveAgentsResult = await getChatHistory(
|
|
109
|
+
context,
|
|
110
|
+
"completed",
|
|
111
|
+
20,
|
|
112
|
+
);
|
|
95
113
|
|
|
96
|
-
if (activeAgentsResult
|
|
97
|
-
const activeAgents = activeAgentsResult
|
|
114
|
+
if (activeAgentsResult && Array.isArray(activeAgentsResult)) {
|
|
115
|
+
const activeAgents = activeAgentsResult;
|
|
98
116
|
|
|
99
117
|
// Create terminals for active agents
|
|
100
118
|
const activeTerminals: TerminalInstance[] = await Promise.all(
|
|
101
|
-
activeAgents.map(async (agent, index) => {
|
|
119
|
+
activeAgents.map(async (agent: any, index: number) => {
|
|
102
120
|
// Load messages for each active agent
|
|
103
121
|
const agentWithMessages = await loadAgentMessages(agent.id);
|
|
104
122
|
|
|
105
123
|
return {
|
|
106
124
|
id: `agent-${agent.id}`,
|
|
107
|
-
title: agent.name ||
|
|
125
|
+
title: agent.name || "New Agent",
|
|
108
126
|
agentId: agent.id,
|
|
109
|
-
options:
|
|
127
|
+
options: {
|
|
128
|
+
...initialOptions,
|
|
129
|
+
// Pass cost information from loaded agent
|
|
130
|
+
totalCost: agentWithMessages?.totalCost,
|
|
131
|
+
totalInputTokenCost: agentWithMessages?.totalInputTokenCost,
|
|
132
|
+
totalOutputTokenCost: agentWithMessages?.totalOutputTokenCost,
|
|
133
|
+
totalCachedTokenCost:
|
|
134
|
+
agentWithMessages?.totalCachedInputTokenCost,
|
|
135
|
+
totalInputTokens: agentWithMessages?.totalInputTokens,
|
|
136
|
+
totalOutputTokens: agentWithMessages?.totalOutputTokens,
|
|
137
|
+
totalCachedTokens: agentWithMessages?.totalCachedInputTokens,
|
|
138
|
+
},
|
|
110
139
|
messages: agentWithMessages?.messages
|
|
111
140
|
? convertAgentMessagesToTerminalMessages(
|
|
112
141
|
agentWithMessages.messages,
|
|
@@ -116,16 +145,15 @@ export function Agents({
|
|
|
116
145
|
}),
|
|
117
146
|
);
|
|
118
147
|
|
|
119
|
-
setTerminals(activeTerminals);
|
|
120
|
-
|
|
121
148
|
// Set the first active terminal as active, or create a new one if none exist
|
|
122
149
|
if (activeTerminals.length > 0) {
|
|
150
|
+
setTerminals(activeTerminals);
|
|
123
151
|
setActiveTerminalId(activeTerminals[0]!.id);
|
|
124
152
|
} else {
|
|
125
153
|
// Create a default terminal if no active agents
|
|
126
154
|
const defaultTerminal: TerminalInstance = {
|
|
127
155
|
id: `terminal-${nextTerminalNumber.current}`,
|
|
128
|
-
title:
|
|
156
|
+
title: "New Agent",
|
|
129
157
|
options: initialOptions,
|
|
130
158
|
};
|
|
131
159
|
setTerminals([defaultTerminal]);
|
|
@@ -134,11 +162,8 @@ export function Agents({
|
|
|
134
162
|
}
|
|
135
163
|
}
|
|
136
164
|
|
|
137
|
-
if (
|
|
138
|
-
inactiveAgentsResult
|
|
139
|
-
inactiveAgentsResult.data
|
|
140
|
-
) {
|
|
141
|
-
setInactiveAgents(inactiveAgentsResult.data);
|
|
165
|
+
if (inactiveAgentsResult && Array.isArray(inactiveAgentsResult)) {
|
|
166
|
+
setInactiveAgents(inactiveAgentsResult);
|
|
142
167
|
}
|
|
143
168
|
} catch (error) {
|
|
144
169
|
console.error("Failed to load agents:", error);
|
|
@@ -160,9 +185,10 @@ export function Agents({
|
|
|
160
185
|
agentId: string,
|
|
161
186
|
): Promise<AgentChat | null> => {
|
|
162
187
|
try {
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
188
|
+
const context = createAgentContext();
|
|
189
|
+
const result = await getAgent(agentId, context);
|
|
190
|
+
if (result && result.id) {
|
|
191
|
+
return result;
|
|
166
192
|
}
|
|
167
193
|
} catch (error) {
|
|
168
194
|
console.error(`Failed to load messages for agent ${agentId}:`, error);
|
|
@@ -181,6 +207,17 @@ export function Agents({
|
|
|
181
207
|
nextTerminalNumber.current++;
|
|
182
208
|
};
|
|
183
209
|
|
|
210
|
+
const createNewAgent = (options: AiTerminalOptions) => {
|
|
211
|
+
const newTerminal: TerminalInstance = {
|
|
212
|
+
id: `terminal-${nextTerminalNumber.current}`,
|
|
213
|
+
title: `Agent ${nextTerminalNumber.current}`,
|
|
214
|
+
options: options,
|
|
215
|
+
};
|
|
216
|
+
setTerminals((prev) => [...prev, newTerminal]);
|
|
217
|
+
setActiveTerminalId(newTerminal.id);
|
|
218
|
+
nextTerminalNumber.current++;
|
|
219
|
+
};
|
|
220
|
+
|
|
184
221
|
const closeTerminal = (terminalId: string) => {
|
|
185
222
|
if (terminals.length <= 1) {
|
|
186
223
|
// Don't close the last terminal
|
|
@@ -218,7 +255,17 @@ export function Agents({
|
|
|
218
255
|
id: `agent-${agent.id}`,
|
|
219
256
|
title: agent.name,
|
|
220
257
|
agentId: agent.id,
|
|
221
|
-
options:
|
|
258
|
+
options: {
|
|
259
|
+
...initialOptions,
|
|
260
|
+
// Pass cost information from loaded agent
|
|
261
|
+
totalCost: agentWithMessages?.totalCost,
|
|
262
|
+
totalInputTokenCost: agentWithMessages?.totalInputTokenCost,
|
|
263
|
+
totalOutputTokenCost: agentWithMessages?.totalOutputTokenCost,
|
|
264
|
+
totalCachedTokenCost: agentWithMessages?.totalCachedInputTokenCost,
|
|
265
|
+
totalInputTokens: agentWithMessages?.totalInputTokens,
|
|
266
|
+
totalOutputTokens: agentWithMessages?.totalOutputTokens,
|
|
267
|
+
totalCachedTokens: agentWithMessages?.totalCachedInputTokens,
|
|
268
|
+
},
|
|
222
269
|
messages: agentWithMessages?.messages
|
|
223
270
|
? convertAgentMessagesToTerminalMessages(agentWithMessages.messages)
|
|
224
271
|
: [],
|
|
@@ -347,6 +394,8 @@ export function Agents({
|
|
|
347
394
|
...terminal.options,
|
|
348
395
|
// Pass the pre-loaded messages to the terminal
|
|
349
396
|
initialMessages: terminal.messages,
|
|
397
|
+
// Pass the agentId to maintain continuity
|
|
398
|
+
agentId: terminal.agentId,
|
|
350
399
|
}}
|
|
351
400
|
onAgentNameUpdate={(name) => {
|
|
352
401
|
// Update the terminal title when agent name is updated
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Sparkles, Send } from "lucide-react";
|
|
5
|
+
import {
|
|
6
|
+
Popover,
|
|
7
|
+
PopoverContent,
|
|
8
|
+
PopoverTrigger,
|
|
9
|
+
} from "../../components/ui/popover";
|
|
10
|
+
import { Button } from "../../components/ui/button";
|
|
11
|
+
import { useEditContext } from "../client/editContext";
|
|
12
|
+
import { AiTerminalOptions } from "./AiTerminal";
|
|
13
|
+
import { Component } from "../pageModel";
|
|
14
|
+
|
|
15
|
+
interface AiPromptPopoverProps {
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
components?: Component[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function AiPromptPopover({
|
|
21
|
+
children,
|
|
22
|
+
components = [],
|
|
23
|
+
}: AiPromptPopoverProps) {
|
|
24
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
25
|
+
const [prompt, setPrompt] = useState("");
|
|
26
|
+
const editContext = useEditContext();
|
|
27
|
+
|
|
28
|
+
const handleSubmit = async () => {
|
|
29
|
+
if (!prompt.trim()) return;
|
|
30
|
+
|
|
31
|
+
// Close the popover
|
|
32
|
+
setIsOpen(false);
|
|
33
|
+
|
|
34
|
+
// Create comprehensive AI options with component context
|
|
35
|
+
let finalPrompt = prompt.trim();
|
|
36
|
+
let hiddenSystemPrompt: string | undefined;
|
|
37
|
+
|
|
38
|
+
if (components.length > 0) {
|
|
39
|
+
// Create detailed component context for the agent
|
|
40
|
+
const componentInfo = components
|
|
41
|
+
.map((comp) => {
|
|
42
|
+
const componentType = comp.type || comp.name || "Unknown";
|
|
43
|
+
const componentId = comp.id;
|
|
44
|
+
const hasContent = comp.items && comp.items.length > 0;
|
|
45
|
+
|
|
46
|
+
return `- ${componentType} (ID: ${componentId})${hasContent ? " with data" : " (empty)"}`;
|
|
47
|
+
})
|
|
48
|
+
.join("\n");
|
|
49
|
+
|
|
50
|
+
finalPrompt = `I'm working with the following ${components.length === 1 ? "component" : "components"}:
|
|
51
|
+
${componentInfo}
|
|
52
|
+
|
|
53
|
+
User request: ${prompt.trim()}`;
|
|
54
|
+
|
|
55
|
+
// Add hidden system prompt with component details for agent context
|
|
56
|
+
const componentDetails = components.map((comp) => ({
|
|
57
|
+
id: comp.id,
|
|
58
|
+
name: comp.name,
|
|
59
|
+
type: comp.type,
|
|
60
|
+
renderingId: comp.rendering?.id,
|
|
61
|
+
items: comp.items?.length || 0,
|
|
62
|
+
placeholder: comp.parentPlaceholder?.name,
|
|
63
|
+
isShared: comp.isShared,
|
|
64
|
+
}));
|
|
65
|
+
|
|
66
|
+
hiddenSystemPrompt = `Working with components: ${JSON.stringify(componentDetails, null, 2)}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Switch to agents view first
|
|
70
|
+
editContext?.switchView("agents");
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
// Execute the prompt directly with a new agent
|
|
74
|
+
await executePromptDirectly(finalPrompt, hiddenSystemPrompt);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error("Failed to execute prompt with new agent:", error);
|
|
77
|
+
// Fallback to the old method if the new one fails
|
|
78
|
+
const aiOptions: AiTerminalOptions = {
|
|
79
|
+
initialPrompt: finalPrompt,
|
|
80
|
+
hiddenSystemPrompt,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Reset the prompt
|
|
85
|
+
setPrompt("");
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const executePromptDirectly = async (
|
|
89
|
+
initialPrompt: string,
|
|
90
|
+
hiddenSystemPrompt?: string,
|
|
91
|
+
) => {
|
|
92
|
+
if (!editContext) return;
|
|
93
|
+
|
|
94
|
+
// Generate a new agent ID
|
|
95
|
+
const newAgentId = crypto.randomUUID();
|
|
96
|
+
|
|
97
|
+
// Create the user message
|
|
98
|
+
const userMessage = {
|
|
99
|
+
id: crypto.randomUUID(),
|
|
100
|
+
content: initialPrompt,
|
|
101
|
+
role: "user",
|
|
102
|
+
name: "user",
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// Create messages array with optional hidden system prompt
|
|
106
|
+
const messages = [
|
|
107
|
+
...(hiddenSystemPrompt
|
|
108
|
+
? [
|
|
109
|
+
{
|
|
110
|
+
role: "system",
|
|
111
|
+
name: "system",
|
|
112
|
+
content: hiddenSystemPrompt,
|
|
113
|
+
id: crypto.randomUUID(),
|
|
114
|
+
},
|
|
115
|
+
]
|
|
116
|
+
: []),
|
|
117
|
+
userMessage,
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
// Import the required functions
|
|
121
|
+
const { executePrompt } = await import("../services/aiService");
|
|
122
|
+
const { createEditorAiContext } = await import("./editorAiContext");
|
|
123
|
+
|
|
124
|
+
// Create AI context
|
|
125
|
+
const aiContext = createEditorAiContext({ editContext });
|
|
126
|
+
|
|
127
|
+
// Execute the prompt with the new agent ID
|
|
128
|
+
await executePrompt(messages, aiContext, {
|
|
129
|
+
sessionId: editContext.sessionId,
|
|
130
|
+
agentId: newAgentId,
|
|
131
|
+
addSelectedComponents: true,
|
|
132
|
+
profileId: "Editor", // Default to Editor profile
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// The agents list will refresh automatically via websockets or periodic refresh
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
139
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
140
|
+
e.preventDefault();
|
|
141
|
+
handleSubmit();
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<Popover
|
|
147
|
+
open={isOpen}
|
|
148
|
+
onOpenChange={setIsOpen}
|
|
149
|
+
enableIframeClickDetection={false}
|
|
150
|
+
>
|
|
151
|
+
<PopoverTrigger asChild>{children}</PopoverTrigger>
|
|
152
|
+
<PopoverContent
|
|
153
|
+
className="w-96 p-4"
|
|
154
|
+
side="bottom"
|
|
155
|
+
align="start"
|
|
156
|
+
onMouseDown={(e) => e.stopPropagation()}
|
|
157
|
+
onClick={(e) => e.stopPropagation()}
|
|
158
|
+
>
|
|
159
|
+
<div className="space-y-3">
|
|
160
|
+
<div className="flex items-center gap-2">
|
|
161
|
+
<Sparkles size={16} className="text-blue-500" />
|
|
162
|
+
<h3 className="font-medium">AI Assistant</h3>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<div className="space-y-2">
|
|
166
|
+
<label htmlFor="ai-prompt" className="text-sm text-gray-700">
|
|
167
|
+
What would you like AI to help you with?
|
|
168
|
+
{components.length > 0 && (
|
|
169
|
+
<span className="mt-1 block text-xs text-gray-500">
|
|
170
|
+
Working with {components.length} selected component
|
|
171
|
+
{components.length === 1 ? "" : "s"}
|
|
172
|
+
</span>
|
|
173
|
+
)}
|
|
174
|
+
</label>
|
|
175
|
+
<textarea
|
|
176
|
+
id="ai-prompt"
|
|
177
|
+
value={prompt}
|
|
178
|
+
onChange={(e) => setPrompt(e.target.value)}
|
|
179
|
+
onKeyDown={handleKeyDown}
|
|
180
|
+
placeholder="Enter your prompt here..."
|
|
181
|
+
className="w-full resize-none rounded-md border border-gray-300 px-3 py-2 focus:border-transparent focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
|
182
|
+
rows={3}
|
|
183
|
+
autoFocus
|
|
184
|
+
/>
|
|
185
|
+
</div>
|
|
186
|
+
|
|
187
|
+
<div className="flex justify-end gap-2">
|
|
188
|
+
<Button
|
|
189
|
+
variant="outline"
|
|
190
|
+
size="sm"
|
|
191
|
+
onClick={() => setIsOpen(false)}
|
|
192
|
+
>
|
|
193
|
+
Cancel
|
|
194
|
+
</Button>
|
|
195
|
+
<Button
|
|
196
|
+
size="sm"
|
|
197
|
+
onClick={handleSubmit}
|
|
198
|
+
disabled={!prompt.trim()}
|
|
199
|
+
className="flex items-center gap-1"
|
|
200
|
+
>
|
|
201
|
+
<Send size={14} />
|
|
202
|
+
Start Agent
|
|
203
|
+
</Button>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
</PopoverContent>
|
|
207
|
+
</Popover>
|
|
208
|
+
);
|
|
209
|
+
}
|