@ryanfw/prompt-orchestration-pipeline 0.11.0 → 0.13.0
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/package.json +11 -1
- package/src/cli/analyze-task.js +51 -0
- package/src/cli/index.js +8 -0
- package/src/components/AddPipelineSidebar.jsx +144 -0
- package/src/components/AnalysisProgressTray.jsx +87 -0
- package/src/components/DAGGrid.jsx +157 -47
- package/src/components/JobTable.jsx +4 -3
- package/src/components/Layout.jsx +142 -139
- package/src/components/MarkdownRenderer.jsx +149 -0
- package/src/components/PipelineDAGGrid.jsx +404 -0
- package/src/components/PipelineTypeTaskSidebar.jsx +96 -0
- package/src/components/SchemaPreviewPanel.jsx +97 -0
- package/src/components/StageTimeline.jsx +36 -0
- package/src/components/TaskAnalysisDisplay.jsx +227 -0
- package/src/components/TaskCreationSidebar.jsx +447 -0
- package/src/components/TaskDetailSidebar.jsx +119 -117
- package/src/components/TaskFilePane.jsx +94 -39
- package/src/components/ui/RestartJobModal.jsx +26 -6
- package/src/components/ui/StopJobModal.jsx +183 -0
- package/src/components/ui/button.jsx +59 -27
- package/src/components/ui/sidebar.jsx +118 -0
- package/src/config/models.js +99 -67
- package/src/core/config.js +11 -4
- package/src/core/lifecycle-policy.js +62 -0
- package/src/core/pipeline-runner.js +312 -217
- package/src/core/status-writer.js +84 -0
- package/src/llm/index.js +129 -9
- package/src/pages/Code.jsx +8 -1
- package/src/pages/PipelineDetail.jsx +84 -2
- package/src/pages/PipelineList.jsx +214 -0
- package/src/pages/PipelineTypeDetail.jsx +234 -0
- package/src/pages/PromptPipelineDashboard.jsx +10 -11
- package/src/providers/deepseek.js +76 -16
- package/src/providers/openai.js +61 -34
- package/src/task-analysis/enrichers/analysis-writer.js +62 -0
- package/src/task-analysis/enrichers/schema-deducer.js +145 -0
- package/src/task-analysis/enrichers/schema-writer.js +74 -0
- package/src/task-analysis/extractors/artifacts.js +137 -0
- package/src/task-analysis/extractors/llm-calls.js +176 -0
- package/src/task-analysis/extractors/stages.js +51 -0
- package/src/task-analysis/index.js +103 -0
- package/src/task-analysis/parser.js +28 -0
- package/src/task-analysis/utils/ast.js +43 -0
- package/src/ui/client/adapters/job-adapter.js +60 -0
- package/src/ui/client/api.js +233 -8
- package/src/ui/client/hooks/useAnalysisProgress.js +145 -0
- package/src/ui/client/hooks/useJobList.js +14 -1
- package/src/ui/client/index.css +64 -0
- package/src/ui/client/main.jsx +4 -0
- package/src/ui/client/sse-fetch.js +120 -0
- package/src/ui/dist/app.js +262 -0
- package/src/ui/dist/assets/index-cjHV9mYW.js +82578 -0
- package/src/ui/dist/assets/index-cjHV9mYW.js.map +1 -0
- package/src/ui/dist/assets/style-CoM9SoQF.css +180 -0
- package/src/ui/dist/favicon.svg +12 -0
- package/src/ui/dist/index.html +2 -2
- package/src/ui/endpoints/create-pipeline-endpoint.js +194 -0
- package/src/ui/endpoints/file-endpoints.js +330 -0
- package/src/ui/endpoints/job-control-endpoints.js +1001 -0
- package/src/ui/endpoints/job-endpoints.js +62 -0
- package/src/ui/endpoints/pipeline-analysis-endpoint.js +246 -0
- package/src/ui/endpoints/pipeline-type-detail-endpoint.js +181 -0
- package/src/ui/endpoints/pipelines-endpoint.js +133 -0
- package/src/ui/endpoints/schema-file-endpoint.js +105 -0
- package/src/ui/endpoints/sse-endpoints.js +223 -0
- package/src/ui/endpoints/state-endpoint.js +85 -0
- package/src/ui/endpoints/task-analysis-endpoint.js +104 -0
- package/src/ui/endpoints/task-creation-endpoint.js +114 -0
- package/src/ui/endpoints/task-save-endpoint.js +101 -0
- package/src/ui/endpoints/upload-endpoints.js +406 -0
- package/src/ui/express-app.js +227 -0
- package/src/ui/lib/analysis-lock.js +67 -0
- package/src/ui/lib/sse.js +30 -0
- package/src/ui/server.js +42 -1880
- package/src/ui/sse-broadcast.js +93 -0
- package/src/ui/utils/http-utils.js +139 -0
- package/src/ui/utils/mime-types.js +196 -0
- package/src/ui/utils/slug.js +31 -0
- package/src/ui/vite.config.js +22 -0
- package/src/ui/watcher.js +28 -2
- package/src/utils/jobs.js +39 -0
- package/src/ui/dist/assets/index-DeDzq-Kk.js +0 -23863
- package/src/ui/dist/assets/style-aBtD_Yrs.css +0 -62
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { Box, Flex, Text, Heading, Select } from "@radix-ui/themes";
|
|
3
|
+
import { Button } from "./button.jsx";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* StopJobModal component for confirming job stop
|
|
7
|
+
* @param {Object} props
|
|
8
|
+
* @param {boolean} props.isOpen - Whether the modal is open
|
|
9
|
+
* @param {Function} props.onClose - Function to call when modal is closed
|
|
10
|
+
* @param {Function} props.onConfirm - Function to call when stop is confirmed (receives jobId)
|
|
11
|
+
* @param {Array} props.runningJobs - Array of running jobs with {id, name, progress?}
|
|
12
|
+
* @param {string} [props.defaultJobId] - Default job ID to pre-select
|
|
13
|
+
* @param {boolean} props.isSubmitting - Whether the stop action is in progress
|
|
14
|
+
*/
|
|
15
|
+
export function StopJobModal({
|
|
16
|
+
isOpen,
|
|
17
|
+
onClose,
|
|
18
|
+
onConfirm,
|
|
19
|
+
runningJobs,
|
|
20
|
+
defaultJobId,
|
|
21
|
+
isSubmitting = false,
|
|
22
|
+
}) {
|
|
23
|
+
const modalRef = useRef(null);
|
|
24
|
+
const [selectedJobId, setSelectedJobId] = useState(defaultJobId || "");
|
|
25
|
+
|
|
26
|
+
// Reset selected job when modal opens/closes
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (isOpen) {
|
|
29
|
+
setSelectedJobId(
|
|
30
|
+
defaultJobId || (runningJobs.length === 1 ? runningJobs[0].id : "")
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}, [isOpen, defaultJobId]);
|
|
34
|
+
|
|
35
|
+
// Handle Escape key to close modal
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const handleKeyDown = (e) => {
|
|
38
|
+
if (e.key === "Escape" && isOpen) {
|
|
39
|
+
e.preventDefault();
|
|
40
|
+
onClose();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
if (isOpen) {
|
|
45
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
46
|
+
// Focus the modal for accessibility
|
|
47
|
+
if (modalRef.current) {
|
|
48
|
+
modalRef.current.focus();
|
|
49
|
+
}
|
|
50
|
+
return () => {
|
|
51
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}, [isOpen, onClose]);
|
|
55
|
+
|
|
56
|
+
// Handle Enter key to confirm when modal is focused
|
|
57
|
+
const handleKeyDown = (e) => {
|
|
58
|
+
if (e.key === "Enter" && !isSubmitting && isOpen && selectedJobId) {
|
|
59
|
+
e.preventDefault();
|
|
60
|
+
onConfirm(selectedJobId);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
if (!isOpen) return null;
|
|
65
|
+
|
|
66
|
+
const handleConfirm = () => {
|
|
67
|
+
if (selectedJobId) {
|
|
68
|
+
onConfirm(selectedJobId);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const selectedJob = runningJobs.find((job) => job.id === selectedJobId);
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<>
|
|
76
|
+
<div
|
|
77
|
+
className="fixed inset-0 z-50 flex items-center justify-center"
|
|
78
|
+
aria-hidden={!isOpen}
|
|
79
|
+
>
|
|
80
|
+
{/* Backdrop */}
|
|
81
|
+
<div
|
|
82
|
+
className="absolute inset-0 bg-black/50"
|
|
83
|
+
onClick={onClose}
|
|
84
|
+
aria-hidden="true"
|
|
85
|
+
/>
|
|
86
|
+
|
|
87
|
+
{/* Modal */}
|
|
88
|
+
<div
|
|
89
|
+
ref={modalRef}
|
|
90
|
+
role="dialog"
|
|
91
|
+
aria-modal="true"
|
|
92
|
+
aria-labelledby="stop-modal-title"
|
|
93
|
+
aria-describedby="stop-modal-description"
|
|
94
|
+
className="relative bg-white rounded-lg shadow-2xl border border-gray-200 max-w-lg w-full mx-4 outline-none"
|
|
95
|
+
style={{ minWidth: "320px", maxWidth: "560px" }}
|
|
96
|
+
tabIndex={-1}
|
|
97
|
+
onKeyDown={handleKeyDown}
|
|
98
|
+
>
|
|
99
|
+
<div className="p-6">
|
|
100
|
+
{/* Header */}
|
|
101
|
+
<Heading
|
|
102
|
+
id="stop-modal-title"
|
|
103
|
+
as="h2"
|
|
104
|
+
size="5"
|
|
105
|
+
className="mb-4 text-gray-900"
|
|
106
|
+
>
|
|
107
|
+
Stop pipeline?
|
|
108
|
+
</Heading>
|
|
109
|
+
|
|
110
|
+
{/* Body */}
|
|
111
|
+
<Box id="stop-modal-description" className="mb-6">
|
|
112
|
+
<Text as="p" className="text-gray-700 mb-4">
|
|
113
|
+
This will stop the running pipeline and reset the current task
|
|
114
|
+
to pending. The pipeline will remain stopped until explicitly
|
|
115
|
+
started or restarted. Files and artifacts are preserved. This
|
|
116
|
+
cannot be undone.
|
|
117
|
+
</Text>
|
|
118
|
+
|
|
119
|
+
{runningJobs.length > 1 && !defaultJobId && (
|
|
120
|
+
<Box className="mb-4">
|
|
121
|
+
<Text as="p" className="text-sm text-gray-600 mb-2">
|
|
122
|
+
Select which job to stop:
|
|
123
|
+
</Text>
|
|
124
|
+
<Select.Root
|
|
125
|
+
value={selectedJobId}
|
|
126
|
+
onValueChange={setSelectedJobId}
|
|
127
|
+
disabled={isSubmitting}
|
|
128
|
+
>
|
|
129
|
+
<Select.Trigger className="w-full" />
|
|
130
|
+
<Select.Content>
|
|
131
|
+
{runningJobs.map((job) => (
|
|
132
|
+
<Select.Item key={job.id} value={job.id}>
|
|
133
|
+
{job.name}{" "}
|
|
134
|
+
{job.progress !== undefined &&
|
|
135
|
+
`(${Math.round(job.progress)}%)`}
|
|
136
|
+
</Select.Item>
|
|
137
|
+
))}
|
|
138
|
+
</Select.Content>
|
|
139
|
+
</Select.Root>
|
|
140
|
+
</Box>
|
|
141
|
+
)}
|
|
142
|
+
|
|
143
|
+
{selectedJob && (
|
|
144
|
+
<Text as="p" className="text-sm text-blue-600 mb-3">
|
|
145
|
+
<strong>Job to stop:</strong> {selectedJob.name}
|
|
146
|
+
{selectedJob.progress !== undefined &&
|
|
147
|
+
` (${Math.round(selectedJob.progress)}%)`}
|
|
148
|
+
</Text>
|
|
149
|
+
)}
|
|
150
|
+
|
|
151
|
+
<Text as="p" className="text-sm text-gray-500 italic">
|
|
152
|
+
Note: The job must be currently running to be stopped.
|
|
153
|
+
</Text>
|
|
154
|
+
</Box>
|
|
155
|
+
|
|
156
|
+
{/* Actions */}
|
|
157
|
+
<Flex gap="3" justify="end">
|
|
158
|
+
<Button
|
|
159
|
+
variant="outline"
|
|
160
|
+
onClick={onClose}
|
|
161
|
+
disabled={isSubmitting}
|
|
162
|
+
className="min-w-[80px]"
|
|
163
|
+
>
|
|
164
|
+
Cancel
|
|
165
|
+
</Button>
|
|
166
|
+
|
|
167
|
+
<Button
|
|
168
|
+
variant="destructive"
|
|
169
|
+
onClick={handleConfirm}
|
|
170
|
+
disabled={!selectedJobId || isSubmitting}
|
|
171
|
+
className="min-w-[80px]"
|
|
172
|
+
>
|
|
173
|
+
{isSubmitting ? "Stopping..." : "Stop"}
|
|
174
|
+
</Button>
|
|
175
|
+
</Flex>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
</>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export default StopJobModal;
|
|
@@ -1,43 +1,75 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { Button as RadixButton } from "@radix-ui/themes";
|
|
3
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Button Component
|
|
5
|
+
*
|
|
6
|
+
* Standardized button component following the Steel Terminal design system.
|
|
7
|
+
* Use this component for all buttons instead of raw <button> tags.
|
|
8
|
+
*
|
|
9
|
+
* @see docs/button-standards.md for usage guidelines
|
|
10
|
+
*
|
|
11
|
+
* @param {string} variant - Button variant: solid, soft, outline, ghost, destructive
|
|
12
|
+
* @param {string} size - Button size: sm, md, lg
|
|
13
|
+
* @param {boolean} loading - Show loading state
|
|
14
|
+
* @param {string} className - Additional CSS classes
|
|
15
|
+
*/
|
|
4
16
|
export function Button({
|
|
5
17
|
variant = "solid",
|
|
6
|
-
size = "
|
|
18
|
+
size = "md",
|
|
19
|
+
loading = false,
|
|
7
20
|
className = "",
|
|
21
|
+
disabled,
|
|
22
|
+
children,
|
|
23
|
+
type = "button",
|
|
8
24
|
...props
|
|
9
25
|
}) {
|
|
10
|
-
//
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
? "solid"
|
|
14
|
-
: variant === "outline"
|
|
15
|
-
? "outline"
|
|
16
|
-
: variant === "ghost"
|
|
17
|
-
? "ghost"
|
|
18
|
-
: variant === "secondary"
|
|
19
|
-
? "soft"
|
|
20
|
-
: variant === "destructive"
|
|
21
|
-
? "solid"
|
|
22
|
-
: "solid";
|
|
26
|
+
// Base classes for all buttons
|
|
27
|
+
const baseClasses =
|
|
28
|
+
"inline-flex items-center justify-center font-medium rounded-md transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed";
|
|
23
29
|
|
|
24
|
-
//
|
|
25
|
-
const
|
|
26
|
-
|
|
30
|
+
// Variant styles using Steel Terminal theme colors
|
|
31
|
+
const variantClasses = {
|
|
32
|
+
solid:
|
|
33
|
+
"bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))] hover:bg-[hsl(var(--primary-hover))] focus:ring-[hsl(var(--primary))]",
|
|
34
|
+
default:
|
|
35
|
+
"bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))] hover:bg-[hsl(var(--primary-hover))] focus:ring-[hsl(var(--primary))]",
|
|
36
|
+
soft: "bg-[hsl(var(--primary))]/10 text-[hsl(var(--primary))] hover:bg-[hsl(var(--primary))]/15 focus:ring-[hsl(var(--primary))] border border-[hsl(var(--primary))]/20",
|
|
37
|
+
outline:
|
|
38
|
+
"border border-[hsl(var(--border))] text-[hsl(var(--secondary-foreground))] bg-transparent hover:bg-[hsl(var(--secondary))] focus:ring-[hsl(var(--ring))]",
|
|
39
|
+
ghost:
|
|
40
|
+
"text-[hsl(var(--muted-foreground))] bg-transparent hover:bg-[hsl(var(--secondary))] hover:text-[hsl(var(--secondary-foreground))] focus:ring-[hsl(var(--ring))]",
|
|
41
|
+
destructive:
|
|
42
|
+
"bg-[hsl(var(--destructive))] text-[hsl(var(--destructive-foreground))] hover:bg-[hsl(var(--destructive))]/90 focus:ring-[hsl(var(--destructive))]",
|
|
43
|
+
};
|
|
27
44
|
|
|
28
|
-
//
|
|
29
|
-
const
|
|
45
|
+
// Size styles
|
|
46
|
+
const sizeClasses = {
|
|
47
|
+
sm: "px-3 py-1.5 text-sm",
|
|
48
|
+
md: "px-4 py-2 text-base",
|
|
49
|
+
lg: "px-6 py-3 text-lg",
|
|
50
|
+
};
|
|
30
51
|
|
|
31
|
-
//
|
|
32
|
-
const
|
|
52
|
+
// Disable button when loading or explicitly disabled
|
|
53
|
+
const isDisabled = disabled || loading;
|
|
54
|
+
|
|
55
|
+
// Combine all classes
|
|
56
|
+
const combinedClassName = `${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]} ${className}`;
|
|
33
57
|
|
|
34
58
|
return (
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
size={radixSize}
|
|
38
|
-
color={color}
|
|
59
|
+
<button
|
|
60
|
+
type={type}
|
|
39
61
|
className={combinedClassName}
|
|
62
|
+
disabled={isDisabled}
|
|
40
63
|
{...props}
|
|
41
|
-
|
|
64
|
+
>
|
|
65
|
+
{loading ? (
|
|
66
|
+
<span className="inline-flex items-center gap-2">
|
|
67
|
+
<span className="animate-spin">⟳</span>
|
|
68
|
+
{children}
|
|
69
|
+
</span>
|
|
70
|
+
) : (
|
|
71
|
+
children
|
|
72
|
+
)}
|
|
73
|
+
</button>
|
|
42
74
|
);
|
|
43
75
|
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import * as Dialog from "@radix-ui/react-dialog";
|
|
2
|
+
import { X } from "lucide-react";
|
|
3
|
+
import { forwardRef } from "react";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Unified Sidebar Component
|
|
7
|
+
*
|
|
8
|
+
* A consistent, accessible slide-over panel with:
|
|
9
|
+
* - Unified styling and animations
|
|
10
|
+
* - Focus trap and keyboard navigation
|
|
11
|
+
* - Backdrop with click-to-close
|
|
12
|
+
* - Consistent z-index management
|
|
13
|
+
* - Steel Terminal theme compliance
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} props - Component props
|
|
16
|
+
* @param {boolean} props.open - Whether sidebar is open
|
|
17
|
+
* @param {Function} props.onOpenChange - Callback when open state changes
|
|
18
|
+
* @param {ReactNode} props.title - Sidebar title
|
|
19
|
+
* @param {string} props.description - Optional description
|
|
20
|
+
* @param {ReactNode} props.children - Sidebar content
|
|
21
|
+
* @param {string} props.className - Additional classes for content area
|
|
22
|
+
* @param {string} props.contentClassName - Additional classes for sidebar panel
|
|
23
|
+
* @param {string} props.headerClassName - Additional classes for header
|
|
24
|
+
* @param {boolean} props.showHeaderBorder - Whether to show header border (default: true)
|
|
25
|
+
*/
|
|
26
|
+
export const Sidebar = forwardRef(
|
|
27
|
+
(
|
|
28
|
+
{
|
|
29
|
+
open,
|
|
30
|
+
onOpenChange,
|
|
31
|
+
title,
|
|
32
|
+
description,
|
|
33
|
+
children,
|
|
34
|
+
className,
|
|
35
|
+
contentClassName,
|
|
36
|
+
headerClassName,
|
|
37
|
+
showHeaderBorder = true,
|
|
38
|
+
...props
|
|
39
|
+
},
|
|
40
|
+
ref
|
|
41
|
+
) => {
|
|
42
|
+
return (
|
|
43
|
+
<Dialog.Root open={open} onOpenChange={onOpenChange}>
|
|
44
|
+
<Dialog.Portal>
|
|
45
|
+
<Dialog.Overlay className="fixed inset-0 bg-black/40 backdrop-blur-sm z-[1999] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0" />
|
|
46
|
+
<Dialog.Content
|
|
47
|
+
ref={ref}
|
|
48
|
+
style={{ backgroundColor: "white" }}
|
|
49
|
+
className={`fixed inset-y-0 right-0 z-[2000] w-full max-w-[900px] min-w-[384px] bg-card shadow-2xl transform transition-all duration-300 ease-in-out data-[state=closed]:translate-x-full data-[state=open]:translate-x-0 data-[state=closed]:animate-out data-[state=open]:animate-in ${contentClassName}`}
|
|
50
|
+
{...props}
|
|
51
|
+
>
|
|
52
|
+
{/* Header */}
|
|
53
|
+
<Dialog.Title
|
|
54
|
+
className={`px-6 py-4 text-lg font-semibold text-foreground ${
|
|
55
|
+
showHeaderBorder ? "border-b" : ""
|
|
56
|
+
} ${headerClassName}`}
|
|
57
|
+
>
|
|
58
|
+
{title}
|
|
59
|
+
</Dialog.Title>
|
|
60
|
+
|
|
61
|
+
{description && (
|
|
62
|
+
<Dialog.Description className="px-6 pb-4 pt-3 text-sm text-muted-foreground">
|
|
63
|
+
{description}
|
|
64
|
+
</Dialog.Description>
|
|
65
|
+
)}
|
|
66
|
+
|
|
67
|
+
{/* Close button */}
|
|
68
|
+
<Dialog.Close asChild>
|
|
69
|
+
<button
|
|
70
|
+
type="button"
|
|
71
|
+
className="absolute top-4 right-4 text-muted-foreground hover:text-foreground transition-colors p-2 rounded-md hover:bg-muted"
|
|
72
|
+
aria-label="Close"
|
|
73
|
+
>
|
|
74
|
+
<X className="h-5 w-5" />
|
|
75
|
+
</button>
|
|
76
|
+
</Dialog.Close>
|
|
77
|
+
|
|
78
|
+
{/* Content */}
|
|
79
|
+
<div className={`flex-1 overflow-y-auto ${className}`}>
|
|
80
|
+
{children}
|
|
81
|
+
</div>
|
|
82
|
+
</Dialog.Content>
|
|
83
|
+
</Dialog.Portal>
|
|
84
|
+
</Dialog.Root>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
Sidebar.displayName = "Sidebar";
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* SidebarFooter - Standard footer area for sidebars
|
|
93
|
+
* Use for action buttons at the bottom of sidebars
|
|
94
|
+
*/
|
|
95
|
+
export const SidebarFooter = ({ children, className }) => {
|
|
96
|
+
return (
|
|
97
|
+
<div className={`border-t p-6 bg-card flex gap-3 ${className}`}>
|
|
98
|
+
{children}
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* SidebarSection - Standard section wrapper
|
|
105
|
+
* Use for grouping related content in sidebars
|
|
106
|
+
*/
|
|
107
|
+
export const SidebarSection = ({ title, children, className }) => {
|
|
108
|
+
return (
|
|
109
|
+
<div className={`p-6 ${className}`}>
|
|
110
|
+
{title && (
|
|
111
|
+
<h3 className="text-base font-semibold text-foreground mb-4">
|
|
112
|
+
{title}
|
|
113
|
+
</h3>
|
|
114
|
+
)}
|
|
115
|
+
{children}
|
|
116
|
+
</div>
|
|
117
|
+
);
|
|
118
|
+
};
|
package/src/config/models.js
CHANGED
|
@@ -1,77 +1,90 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Canonical model configuration for prompt orchestration pipeline.
|
|
3
3
|
* This module serves as single source of truth for all model metadata.
|
|
4
|
+
*
|
|
5
|
+
* Last updated: December 2025
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
8
|
// Model alias constants grouped by provider
|
|
7
9
|
export const ModelAlias = Object.freeze({
|
|
8
|
-
// DeepSeek
|
|
10
|
+
// DeepSeek (V3.2-Exp unified pricing as of Sept 2025)
|
|
9
11
|
DEEPSEEK_CHAT: "deepseek:chat",
|
|
10
12
|
DEEPSEEK_REASONER: "deepseek:reasoner",
|
|
11
13
|
|
|
12
|
-
// OpenAI
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
// OpenAI (GPT-5.2 flagship as of Dec 2025)
|
|
15
|
+
OPENAI_GPT_5_2: "openai:gpt-5.2", // NEW: Current flagship
|
|
16
|
+
OPENAI_GPT_5_2_PRO: "openai:gpt-5.2-pro", // NEW: High-compute tier
|
|
17
|
+
OPENAI_GPT_5_1: "openai:gpt-5.1", // NEW: Previous flagship (being sunset)
|
|
18
|
+
OPENAI_GPT_5: "openai:gpt-5", // Stable, still available
|
|
17
19
|
OPENAI_GPT_5_MINI: "openai:gpt-5-mini",
|
|
18
20
|
OPENAI_GPT_5_NANO: "openai:gpt-5-nano",
|
|
19
21
|
|
|
20
22
|
// Legacy aliases for backward compatibility (tests)
|
|
23
|
+
OPENAI_GPT_4_1: "openai:gpt-4.1", // Updated: GPT-4.1 replaced GPT-4 Turbo
|
|
21
24
|
OPENAI_GPT_4: "openai:gpt-4",
|
|
22
|
-
OPENAI_GPT_4_TURBO: "openai:gpt-4-turbo",
|
|
23
25
|
|
|
24
|
-
// Google Gemini
|
|
26
|
+
// Google Gemini (Gemini 3 series released Dec 2025)
|
|
27
|
+
GEMINI_3_PRO: "gemini:pro-3", // NEW: Latest flagship
|
|
28
|
+
GEMINI_3_FLASH: "gemini:flash-3", // NEW: Released Dec 17, 2025
|
|
25
29
|
GEMINI_2_5_PRO: "gemini:pro-2.5",
|
|
26
30
|
GEMINI_2_5_FLASH: "gemini:flash-2.5",
|
|
27
31
|
GEMINI_2_5_FLASH_LITE: "gemini:flash-2.5-lite",
|
|
28
|
-
GEMINI_2_5_FLASH_IMAGE: "gemini:flash-2.5-image",
|
|
29
32
|
|
|
30
|
-
// Z.ai (formerly Zhipu) -
|
|
33
|
+
// Z.ai (formerly Zhipu) - GLM-4.6V released Dec 2025
|
|
34
|
+
ZAI_GLM_4_6V: "zhipu:glm-4.6v", // NEW: Vision-language model
|
|
31
35
|
ZAI_GLM_4_6: "zhipu:glm-4.6",
|
|
32
36
|
ZAI_GLM_4_5: "zhipu:glm-4.5",
|
|
33
37
|
ZAI_GLM_4_5_AIR: "zhipu:glm-4.5-air",
|
|
34
38
|
|
|
35
|
-
// Anthropic
|
|
39
|
+
// Anthropic (Opus 4.5 released Nov 2025)
|
|
40
|
+
ANTHROPIC_OPUS_4_5: "anthropic:opus-4-5", // NEW: Current flagship
|
|
36
41
|
ANTHROPIC_SONNET_4_5: "anthropic:sonnet-4-5",
|
|
37
42
|
ANTHROPIC_HAIKU_4_5: "anthropic:haiku-4-5",
|
|
38
|
-
ANTHROPIC_OPUS_4_1: "anthropic:opus-4-1",
|
|
43
|
+
ANTHROPIC_OPUS_4_1: "anthropic:opus-4-1", // Legacy, still available
|
|
39
44
|
});
|
|
40
45
|
|
|
41
46
|
// Consolidated model configuration with pricing metadata
|
|
42
47
|
export const MODEL_CONFIG = Object.freeze({
|
|
43
|
-
// DeepSeek (2025)
|
|
48
|
+
// ─── DeepSeek (2025) ───
|
|
49
|
+
// V3.2-Exp unified pricing as of Sept 29, 2025 - 50% price reduction
|
|
44
50
|
[ModelAlias.DEEPSEEK_CHAT]: {
|
|
45
51
|
provider: "deepseek",
|
|
46
|
-
model: "deepseek-chat", // V3.2
|
|
47
|
-
tokenCostInPerMillion: 0.
|
|
48
|
-
tokenCostOutPerMillion:
|
|
52
|
+
model: "deepseek-chat", // V3.2-Exp (non-thinking mode)
|
|
53
|
+
tokenCostInPerMillion: 0.28, // Updated: cache miss price
|
|
54
|
+
tokenCostOutPerMillion: 0.42, // Updated: unified output price
|
|
49
55
|
},
|
|
50
56
|
[ModelAlias.DEEPSEEK_REASONER]: {
|
|
51
57
|
provider: "deepseek",
|
|
52
|
-
model: "deepseek-reasoner", //
|
|
53
|
-
tokenCostInPerMillion: 0.
|
|
54
|
-
tokenCostOutPerMillion:
|
|
58
|
+
model: "deepseek-reasoner", // V3.2-Exp (thinking mode)
|
|
59
|
+
tokenCostInPerMillion: 0.28, // Updated: same as chat now
|
|
60
|
+
tokenCostOutPerMillion: 0.42, // Updated: unified pricing
|
|
55
61
|
},
|
|
56
62
|
|
|
57
|
-
//
|
|
58
|
-
|
|
63
|
+
// ─── OpenAI (2025) ───
|
|
64
|
+
// GPT-5.2 released Dec 2025 as new flagship
|
|
65
|
+
[ModelAlias.OPENAI_GPT_5_2]: {
|
|
59
66
|
provider: "openai",
|
|
60
|
-
model: "gpt-5", //
|
|
61
|
-
tokenCostInPerMillion: 1.
|
|
62
|
-
tokenCostOutPerMillion:
|
|
67
|
+
model: "gpt-5.2", // Current flagship for coding/agentic tasks
|
|
68
|
+
tokenCostInPerMillion: 1.75,
|
|
69
|
+
tokenCostOutPerMillion: 14.0,
|
|
63
70
|
},
|
|
64
|
-
[ModelAlias.
|
|
71
|
+
[ModelAlias.OPENAI_GPT_5_2_PRO]: {
|
|
65
72
|
provider: "openai",
|
|
66
|
-
model: "gpt-5-
|
|
67
|
-
tokenCostInPerMillion:
|
|
68
|
-
tokenCostOutPerMillion:
|
|
73
|
+
model: "gpt-5.2-pro", // Maximum intelligence tier
|
|
74
|
+
tokenCostInPerMillion: 17.5, // Estimated based on prior Pro pricing
|
|
75
|
+
tokenCostOutPerMillion: 140.0,
|
|
69
76
|
},
|
|
70
|
-
[ModelAlias.
|
|
77
|
+
[ModelAlias.OPENAI_GPT_5_1]: {
|
|
71
78
|
provider: "openai",
|
|
72
|
-
model: "gpt-5
|
|
73
|
-
tokenCostInPerMillion:
|
|
74
|
-
tokenCostOutPerMillion:
|
|
79
|
+
model: "gpt-5.1", // Previous flagship, being sunset from ChatGPT
|
|
80
|
+
tokenCostInPerMillion: 1.5,
|
|
81
|
+
tokenCostOutPerMillion: 12.0,
|
|
82
|
+
},
|
|
83
|
+
[ModelAlias.OPENAI_GPT_5]: {
|
|
84
|
+
provider: "openai",
|
|
85
|
+
model: "gpt-5", // Stable, still available
|
|
86
|
+
tokenCostInPerMillion: 1.25,
|
|
87
|
+
tokenCostOutPerMillion: 10.0,
|
|
75
88
|
},
|
|
76
89
|
[ModelAlias.OPENAI_GPT_5_MINI]: {
|
|
77
90
|
provider: "openai",
|
|
@@ -86,24 +99,37 @@ export const MODEL_CONFIG = Object.freeze({
|
|
|
86
99
|
tokenCostOutPerMillion: 0.4,
|
|
87
100
|
},
|
|
88
101
|
|
|
89
|
-
// Legacy models for backward compatibility
|
|
102
|
+
// Legacy models for backward compatibility
|
|
103
|
+
[ModelAlias.OPENAI_GPT_4_1]: {
|
|
104
|
+
provider: "openai",
|
|
105
|
+
model: "gpt-4.1", // Replaced GPT-4 Turbo
|
|
106
|
+
tokenCostInPerMillion: 2.0,
|
|
107
|
+
tokenCostOutPerMillion: 8.0,
|
|
108
|
+
},
|
|
90
109
|
[ModelAlias.OPENAI_GPT_4]: {
|
|
91
110
|
provider: "openai",
|
|
92
111
|
model: "gpt-4",
|
|
93
112
|
tokenCostInPerMillion: 0.5,
|
|
94
113
|
tokenCostOutPerMillion: 2.0,
|
|
95
114
|
},
|
|
96
|
-
[ModelAlias.OPENAI_GPT_4_TURBO]: {
|
|
97
|
-
provider: "openai",
|
|
98
|
-
model: "gpt-4-turbo",
|
|
99
|
-
tokenCostInPerMillion: 0.3,
|
|
100
|
-
tokenCostOutPerMillion: 1.0,
|
|
101
|
-
},
|
|
102
115
|
|
|
103
|
-
//
|
|
116
|
+
// ─── Google Gemini (2025) ───
|
|
117
|
+
// Gemini 3 series released Nov-Dec 2025
|
|
118
|
+
[ModelAlias.GEMINI_3_PRO]: {
|
|
119
|
+
provider: "gemini",
|
|
120
|
+
model: "gemini-3-pro-preview", // Most intelligent model
|
|
121
|
+
tokenCostInPerMillion: 2.0, // ≤200k tokens
|
|
122
|
+
tokenCostOutPerMillion: 12.0,
|
|
123
|
+
},
|
|
124
|
+
[ModelAlias.GEMINI_3_FLASH]: {
|
|
125
|
+
provider: "gemini",
|
|
126
|
+
model: "gemini-3-flash-preview", // Released Dec 17, 2025
|
|
127
|
+
tokenCostInPerMillion: 0.5,
|
|
128
|
+
tokenCostOutPerMillion: 3.0,
|
|
129
|
+
},
|
|
104
130
|
[ModelAlias.GEMINI_2_5_PRO]: {
|
|
105
131
|
provider: "gemini",
|
|
106
|
-
model: "gemini-2.5-pro", // ≤200k input tier
|
|
132
|
+
model: "gemini-2.5-pro", // ≤200k input tier; >200k is 2x
|
|
107
133
|
tokenCostInPerMillion: 1.25,
|
|
108
134
|
tokenCostOutPerMillion: 10.0,
|
|
109
135
|
},
|
|
@@ -119,51 +145,57 @@ export const MODEL_CONFIG = Object.freeze({
|
|
|
119
145
|
tokenCostInPerMillion: 0.1,
|
|
120
146
|
tokenCostOutPerMillion: 0.4,
|
|
121
147
|
},
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
148
|
+
|
|
149
|
+
// ─── Z.ai (formerly Zhipu) ───
|
|
150
|
+
// GLM-4.6V released Dec 8, 2025 with 50% API price cut
|
|
151
|
+
[ModelAlias.ZAI_GLM_4_6V]: {
|
|
152
|
+
provider: "zhipu",
|
|
153
|
+
model: "glm-4.6v", // Vision-language model (106B)
|
|
126
154
|
tokenCostInPerMillion: 0.3,
|
|
127
|
-
tokenCostOutPerMillion:
|
|
155
|
+
tokenCostOutPerMillion: 0.9,
|
|
128
156
|
},
|
|
129
|
-
|
|
130
|
-
// — Z.ai (formerly Zhipu) —
|
|
131
157
|
[ModelAlias.ZAI_GLM_4_6]: {
|
|
132
158
|
provider: "zhipu",
|
|
133
|
-
model: "glm-4.6",
|
|
134
|
-
tokenCostInPerMillion: 0.
|
|
135
|
-
tokenCostOutPerMillion:
|
|
159
|
+
model: "glm-4.6", // Released Sept 2025
|
|
160
|
+
tokenCostInPerMillion: 0.3, // Updated: price cuts
|
|
161
|
+
tokenCostOutPerMillion: 0.9,
|
|
136
162
|
},
|
|
137
163
|
[ModelAlias.ZAI_GLM_4_5]: {
|
|
138
164
|
provider: "zhipu",
|
|
139
165
|
model: "glm-4.5",
|
|
140
|
-
tokenCostInPerMillion: 0.
|
|
141
|
-
tokenCostOutPerMillion:
|
|
166
|
+
tokenCostInPerMillion: 0.11, // Updated: aggressive pricing
|
|
167
|
+
tokenCostOutPerMillion: 0.28,
|
|
142
168
|
},
|
|
143
169
|
[ModelAlias.ZAI_GLM_4_5_AIR]: {
|
|
144
170
|
provider: "zhipu",
|
|
145
|
-
model: "glm-4.5-air",
|
|
146
|
-
tokenCostInPerMillion: 0.
|
|
147
|
-
tokenCostOutPerMillion:
|
|
171
|
+
model: "glm-4.5-air", // Lightweight variant
|
|
172
|
+
tokenCostInPerMillion: 0.05,
|
|
173
|
+
tokenCostOutPerMillion: 0.15,
|
|
148
174
|
},
|
|
149
175
|
|
|
150
|
-
//
|
|
151
|
-
//
|
|
176
|
+
// ─── Anthropic ───
|
|
177
|
+
// Claude Opus 4.5 released Nov 24, 2025
|
|
178
|
+
[ModelAlias.ANTHROPIC_OPUS_4_5]: {
|
|
179
|
+
provider: "anthropic",
|
|
180
|
+
model: "claude-opus-4-5-20251101", // Current flagship
|
|
181
|
+
tokenCostInPerMillion: 5.0, // Significant reduction from Opus 4.1
|
|
182
|
+
tokenCostOutPerMillion: 25.0,
|
|
183
|
+
},
|
|
152
184
|
[ModelAlias.ANTHROPIC_SONNET_4_5]: {
|
|
153
185
|
provider: "anthropic",
|
|
154
|
-
model: "claude-sonnet-4-5-20250929",
|
|
186
|
+
model: "claude-sonnet-4-5-20250929",
|
|
155
187
|
tokenCostInPerMillion: 3.0,
|
|
156
188
|
tokenCostOutPerMillion: 15.0,
|
|
157
189
|
},
|
|
158
190
|
[ModelAlias.ANTHROPIC_HAIKU_4_5]: {
|
|
159
191
|
provider: "anthropic",
|
|
160
|
-
model: "claude-haiku-4-5-
|
|
161
|
-
tokenCostInPerMillion: 0
|
|
162
|
-
tokenCostOutPerMillion:
|
|
192
|
+
model: "claude-haiku-4-5-20251001",
|
|
193
|
+
tokenCostInPerMillion: 1.0, // Updated from 0.25
|
|
194
|
+
tokenCostOutPerMillion: 5.0, // Updated from 1.25
|
|
163
195
|
},
|
|
164
196
|
[ModelAlias.ANTHROPIC_OPUS_4_1]: {
|
|
165
197
|
provider: "anthropic",
|
|
166
|
-
model: "claude-opus-4-1-
|
|
198
|
+
model: "claude-opus-4-1-20250805", // Legacy, still available
|
|
167
199
|
tokenCostInPerMillion: 15.0,
|
|
168
200
|
tokenCostOutPerMillion: 75.0,
|
|
169
201
|
},
|
|
@@ -175,10 +207,10 @@ export const VALID_MODEL_ALIASES = new Set(Object.keys(MODEL_CONFIG));
|
|
|
175
207
|
// Default model alias for each provider (used when no model specified)
|
|
176
208
|
export const DEFAULT_MODEL_BY_PROVIDER = Object.freeze({
|
|
177
209
|
deepseek: ModelAlias.DEEPSEEK_CHAT,
|
|
178
|
-
openai: ModelAlias.
|
|
179
|
-
gemini: ModelAlias.
|
|
210
|
+
openai: ModelAlias.OPENAI_GPT_5_2, // Updated: GPT-5.2 is new default
|
|
211
|
+
gemini: ModelAlias.GEMINI_3_FLASH, // Updated: Gemini 3 Flash is new default
|
|
180
212
|
zhipu: ModelAlias.ZAI_GLM_4_6,
|
|
181
|
-
anthropic: ModelAlias.
|
|
213
|
+
anthropic: ModelAlias.ANTHROPIC_OPUS_4_5, // Updated: Opus 4.5 available at better price
|
|
182
214
|
});
|
|
183
215
|
|
|
184
216
|
/**
|
|
@@ -245,7 +277,7 @@ export function buildProviderFunctionsIndex() {
|
|
|
245
277
|
|
|
246
278
|
/**
|
|
247
279
|
* Pre-built provider functions index for convenience.
|
|
248
|
-
* Uses dotted style: llm.anthropic.
|
|
280
|
+
* Uses dotted style: llm.anthropic.opus45, llm.openai.gpt52, etc.
|
|
249
281
|
*/
|
|
250
282
|
export const PROVIDER_FUNCTIONS = buildProviderFunctionsIndex();
|
|
251
283
|
|