chore: removed create recipe from session modal and rest api (#9916)

This commit is contained in:
Lifei Zhou
2026-06-22 10:49:55 +10:00
committed by GitHub
parent 2712c066bc
commit b893b5f24e
18 changed files with 3 additions and 1507 deletions
-4
View File
@@ -459,7 +459,6 @@ derive_utoipa!(IconTheme as IconThemeSchema);
super::routes::schedule::kill_running_job,
super::routes::schedule::inspect_running_job,
super::routes::schedule::sessions_handler,
super::routes::recipe::create_recipe,
super::routes::recipe::encode_recipe,
super::routes::recipe::decode_recipe,
super::routes::recipe::scan_recipe,
@@ -597,9 +596,6 @@ derive_utoipa!(IconTheme as IconThemeSchema);
super::routes::schedule::ListSchedulesResponse,
super::routes::schedule::SessionsQuery,
super::routes::schedule::SessionDisplayInfo,
super::routes::recipe::CreateRecipeRequest,
super::routes::recipe::AuthorRequest,
super::routes::recipe::CreateRecipeResponse,
super::routes::recipe::EncodeRecipeRequest,
super::routes::recipe::EncodeRecipeResponse,
super::routes::recipe::DecodeRecipeRequest,
-98
View File
@@ -43,27 +43,6 @@ use crate::routes::recipe_utils::{
};
use crate::state::AppState;
#[derive(Debug, Deserialize, ToSchema)]
pub struct CreateRecipeRequest {
session_id: String,
#[serde(default)]
author: Option<AuthorRequest>,
}
#[derive(Debug, Deserialize, ToSchema)]
pub struct AuthorRequest {
#[serde(default)]
contact: Option<String>,
#[serde(default)]
metadata: Option<String>,
}
#[derive(Debug, Serialize, ToSchema)]
pub struct CreateRecipeResponse {
recipe: Option<Recipe>,
error: Option<String>,
}
#[derive(Debug, Deserialize, ToSchema)]
pub struct EncodeRecipeRequest {
recipe: Recipe,
@@ -148,82 +127,6 @@ pub struct RecipeToYamlResponse {
yaml: String,
}
#[utoipa::path(
post,
path = "/recipes/create",
request_body = CreateRecipeRequest,
responses(
(status = 200, description = "Recipe created successfully", body = CreateRecipeResponse),
(status = 400, description = "Bad request"),
(status = 412, description = "Precondition failed - Agent not available"),
(status = 500, description = "Internal server error")
),
tag = "Recipe Management"
)]
async fn create_recipe(
State(state): State<Arc<AppState>>,
Json(request): Json<CreateRecipeRequest>,
) -> Result<Json<CreateRecipeResponse>, StatusCode> {
tracing::info!(
"Recipe creation request received for session_id: {}",
request.session_id
);
let session = match state
.session_manager()
.get_session(&request.session_id, true)
.await
{
Ok(session) => session,
Err(e) => {
tracing::error!("Failed to get session: {}", e);
return Err(StatusCode::INTERNAL_SERVER_ERROR);
}
};
let conversation = match session.conversation.clone() {
Some(conversation) => conversation,
None => {
let error_message = "Session has no conversation".to_string();
let error_response = CreateRecipeResponse {
recipe: None,
error: Some(error_message),
};
return Ok(Json(error_response));
}
};
let agent = state.get_agent_for_route(request.session_id).await?;
let recipe_result = agent.create_recipe(&session.id, conversation).await;
match recipe_result {
Ok(mut recipe) => {
if let Some(author_req) = request.author {
recipe.author = Some(goose::recipe::Author {
contact: author_req.contact,
metadata: author_req.metadata,
});
}
Ok(Json(CreateRecipeResponse {
recipe: Some(recipe),
error: None,
}))
}
Err(e) => {
tracing::error!("Error details: {:?}", e);
#[cfg(feature = "telemetry")]
goose::posthog::emit_error("recipe_create_failed", &e.to_string());
let error_response = CreateRecipeResponse {
recipe: None,
error: Some(format!("Failed to create recipe: {}", e)),
};
Ok(Json(error_response))
}
}
}
#[utoipa::path(
post,
path = "/recipes/encode",
@@ -571,7 +474,6 @@ async fn recipe_to_yaml(
pub fn routes(state: Arc<AppState>) -> Router {
Router::new()
.route("/recipes/create", post(create_recipe))
.route("/recipes/encode", post(encode_recipe))
.route("/recipes/decode", post(decode_recipe))
.route("/recipes/scan", post(scan_recipe))
-88
View File
@@ -2418,45 +2418,6 @@
}
}
},
"/recipes/create": {
"post": {
"tags": [
"Recipe Management"
],
"operationId": "create_recipe",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateRecipeRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Recipe created successfully",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateRecipeResponse"
}
}
}
},
"400": {
"description": "Bad request"
},
"412": {
"description": "Precondition failed - Agent not available"
},
"500": {
"description": "Internal server error"
}
}
}
},
"/recipes/decode": {
"post": {
"tags": [
@@ -3984,19 +3945,6 @@
}
}
},
"AuthorRequest": {
"type": "object",
"properties": {
"contact": {
"type": "string",
"nullable": true
},
"metadata": {
"type": "string",
"nullable": true
}
}
},
"CallToolRequest": {
"type": "object",
"required": [
@@ -4482,42 +4430,6 @@
}
}
},
"CreateRecipeRequest": {
"type": "object",
"required": [
"session_id"
],
"properties": {
"author": {
"allOf": [
{
"$ref": "#/components/schemas/AuthorRequest"
}
],
"nullable": true
},
"session_id": {
"type": "string"
}
}
},
"CreateRecipeResponse": {
"type": "object",
"properties": {
"error": {
"type": "string",
"nullable": true
},
"recipe": {
"allOf": [
{
"$ref": "#/components/schemas/Recipe"
}
],
"nullable": true
}
}
},
"CreateScheduleRequest": {
"type": "object",
"required": [
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-46
View File
@@ -42,11 +42,6 @@ export type Author = {
metadata?: string | null;
};
export type AuthorRequest = {
contact?: string | null;
metadata?: string | null;
};
export type CallToolRequest = {
arguments: unknown;
name: string;
@@ -178,16 +173,6 @@ export type CreateCustomProviderResponse = {
provider_name: string;
};
export type CreateRecipeRequest = {
author?: AuthorRequest | null;
session_id: string;
};
export type CreateRecipeResponse = {
error?: string | null;
recipe?: Recipe | null;
};
export type CreateScheduleRequest = {
cron: string;
id: string;
@@ -3645,37 +3630,6 @@ export type McpUiProxyResponses = {
200: unknown;
};
export type CreateRecipeData = {
body: CreateRecipeRequest;
path?: never;
query?: never;
url: '/recipes/create';
};
export type CreateRecipeErrors = {
/**
* Bad request
*/
400: unknown;
/**
* Precondition failed - Agent not available
*/
412: unknown;
/**
* Internal server error
*/
500: unknown;
};
export type CreateRecipeResponses = {
/**
* Recipe created successfully
*/
200: CreateRecipeResponse;
};
export type CreateRecipeResponse2 = CreateRecipeResponses[keyof CreateRecipeResponses];
export type DecodeRecipeData = {
body: DecodeRecipeRequest;
path?: never;
-46
View File
@@ -29,9 +29,6 @@ import { useToolCount } from './alerts/useToolCount';
import { getThinkingMessage, getTextAndImageContent } from '../types/message';
import ParameterInputModal from './ParameterInputModal';
import { substituteParameters } from '../utils/parameterSubstitution';
import CreateRecipeFromSessionModal from './recipes/CreateRecipeFromSessionModal';
import { toastSuccess } from '../toasts';
import { Recipe } from '../recipe';
import { useAutoSubmit } from '../hooks/useAutoSubmit';
import { Goose } from './icons';
import EnvironmentBadge from './GooseSidebar/EnvironmentBadge';
@@ -46,18 +43,6 @@ const i18n = defineMessages({
id: 'baseChat.goHome',
defaultMessage: 'Go home',
},
noSession: {
id: 'baseChat.noSession',
defaultMessage: 'No Session',
},
recipeCreatedTitle: {
id: 'baseChat.recipeCreatedTitle',
defaultMessage: 'Recipe created successfully!',
},
recipeCreatedMessage: {
id: 'baseChat.recipeCreatedMessage',
defaultMessage: '"{title}" has been saved and is ready to use.',
},
});
interface BaseChatProps {
@@ -101,7 +86,6 @@ export default function BaseChat({
const contentClassName = cn('pr-1 pb-10 pt-12', (isMobile || isNavCollapsed) && 'pt-16');
const { droppedFiles, setDroppedFiles, handleDrop, handleDragOver } = useFileDrop();
const onStreamFinish = useCallback(() => {}, []);
const [isCreateRecipeModalOpen, setIsCreateRecipeModalOpen] = useState(false);
const {
session,
@@ -312,15 +296,6 @@ export default function BaseChat({
return undefined;
}, [isActiveSession, sessionId, chatState]);
useEffect(() => {
const handleMakeAgent = () => {
setIsCreateRecipeModalOpen(true);
};
window.addEventListener('make-agent-from-chat', handleMakeAgent);
return () => window.removeEventListener('make-agent-from-chat', handleMakeAgent);
}, []);
useEffect(() => {
const handleSessionForked = (event: Event) => {
const customEvent = event as CustomEvent<{
@@ -352,20 +327,6 @@ export default function BaseChat({
};
}, [location.pathname, navigate]);
const handleRecipeCreated = (recipe: Recipe) => {
toastSuccess({
title: intl.formatMessage(i18n.recipeCreatedTitle),
msg: intl.formatMessage(i18n.recipeCreatedMessage, { title: recipe.title }),
});
};
const chat: ChatType = {
messages,
recipe,
sessionId,
name: session?.name || intl.formatMessage(i18n.noSession),
};
const lastSetNameRef = useRef<string>('');
useEffect(() => {
@@ -597,13 +558,6 @@ export default function BaseChat({
}
/>
)}
<CreateRecipeFromSessionModal
isOpen={isCreateRecipeModalOpen}
onClose={() => setIsCreateRecipeModalOpen(false)}
sessionId={chat.sessionId}
onRecipeCreated={handleRecipeCreated}
/>
</div>
);
}
-4
View File
@@ -162,10 +162,6 @@ const i18n = defineMessages({
id: 'chatInput.viewEditRecipe',
defaultMessage: 'View/Edit Recipe',
},
createRecipeFromSession: {
id: 'chatInput.createRecipeFromSession',
defaultMessage: 'Create Recipe from Session',
},
});
interface ChatInputProps {
@@ -1,411 +0,0 @@
import { useState, useEffect } from 'react';
import { useForm } from '@tanstack/react-form';
import { Recipe } from '../../recipe';
import { Geese } from '../icons/Geese';
import { X, Save, Play, Loader2 } from 'lucide-react';
import { Button } from '../ui/button';
import { RecipeFormFields } from './shared/RecipeFormFields';
import { RecipeFormData } from './shared/recipeFormSchema';
import { createRecipe } from '../../api/sdk.gen';
import { RecipeParameter } from './shared/recipeFormSchema';
import { toastError } from '../../toasts';
import { saveRecipe } from '../../recipe/recipe_management';
import { errorMessage } from '../../utils/conversionUtils';
import { defineMessages, useIntl } from '../../i18n';
const i18n = defineMessages({
title: {
id: 'createRecipeFromSession.title',
defaultMessage: 'Create Recipe from Session',
},
subtitle: {
id: 'createRecipeFromSession.subtitle',
defaultMessage: 'Create a reusable recipe based on your current conversation.',
},
analyzingTitle: {
id: 'createRecipeFromSession.analyzingTitle',
defaultMessage: 'Analyzing your conversation',
},
stageReading: {
id: 'createRecipeFromSession.stageReading',
defaultMessage: 'Reading your conversation...',
},
stageIdentifying: {
id: 'createRecipeFromSession.stageIdentifying',
defaultMessage: 'Identifying key patterns...',
},
stageExtracting: {
id: 'createRecipeFromSession.stageExtracting',
defaultMessage: 'Extracting main topics...',
},
stageGenerating: {
id: 'createRecipeFromSession.stageGenerating',
defaultMessage: 'Generating recipe structure...',
},
stageFinalizing: {
id: 'createRecipeFromSession.stageFinalizing',
defaultMessage: 'Finalizing details...',
},
stageComplete: {
id: 'createRecipeFromSession.stageComplete',
defaultMessage: 'Complete!',
},
extractingInsights: {
id: 'createRecipeFromSession.extractingInsights',
defaultMessage: 'Extracting insights from your chat',
},
cancel: {
id: 'createRecipeFromSession.cancel',
defaultMessage: 'Cancel',
},
creating: {
id: 'createRecipeFromSession.creating',
defaultMessage: 'Creating...',
},
createRecipe: {
id: 'createRecipeFromSession.createRecipe',
defaultMessage: 'Create Recipe',
},
createAndRunRecipe: {
id: 'createRecipeFromSession.createAndRunRecipe',
defaultMessage: 'Create & Run Recipe',
},
failedToCreateTitle: {
id: 'createRecipeFromSession.failedToCreateTitle',
defaultMessage: 'Failed to create recipe',
},
failedToCreateDefaultMsg: {
id: 'createRecipeFromSession.failedToCreateDefaultMsg',
defaultMessage: 'An unexpected error occurred while creating the recipe. Please try again.',
},
});
interface CreateRecipeFromSessionModalProps {
isOpen: boolean;
onClose: () => void;
sessionId: string;
onRecipeCreated?: (recipe: Recipe) => void;
}
export default function CreateRecipeFromSessionModal({
isOpen,
onClose,
sessionId,
onRecipeCreated,
}: CreateRecipeFromSessionModalProps) {
const intl = useIntl();
const [isCreating, setIsCreating] = useState(false);
const [isAnalyzing, setIsAnalyzing] = useState(false);
const [analysisStage, setAnalysisStage] = useState<string>('');
const [hasAnalyzed, setHasAnalyzed] = useState(false);
// Initialize form with empty values for new recipe
const form = useForm({
defaultValues: {
title: '',
description: '',
instructions: '',
prompt: '',
activities: [] as string[],
parameters: [] as RecipeParameter[],
jsonSchema: '',
subRecipes: [],
recipeName: '',
global: true,
} as RecipeFormData,
onSubmit: async ({ value }) => {
await handleCreateRecipe(value);
},
});
// Track form validity with state to make it reactive
const [isFormValid, setIsFormValid] = useState(false);
// Analyze messages and prefill form when modal opens
useEffect(() => {
if (isOpen && sessionId && !hasAnalyzed) {
setIsAnalyzing(true);
// Create a sequence of analysis stages for better UX
const stages = [
intl.formatMessage(i18n.stageReading),
intl.formatMessage(i18n.stageIdentifying),
intl.formatMessage(i18n.stageExtracting),
intl.formatMessage(i18n.stageGenerating),
intl.formatMessage(i18n.stageFinalizing),
];
let currentStageIndex = 0;
setAnalysisStage(stages[0]);
// Update stage every 800ms
const stageInterval = setInterval(() => {
currentStageIndex = (currentStageIndex + 1) % stages.length;
setAnalysisStage(stages[currentStageIndex]);
}, 800);
// Call the backend to analyze messages and create a recipe
createRecipe({
body: { session_id: sessionId },
throwOnError: true,
})
.then((response) => {
clearInterval(stageInterval);
setAnalysisStage(intl.formatMessage(i18n.stageComplete));
if (response.data?.recipe) {
const recipe = response.data.recipe;
// Prefill the form with the analyzed recipe information
form.setFieldValue('title', recipe.title || '');
form.setFieldValue('description', recipe.description || '');
form.setFieldValue('instructions', recipe.instructions || '');
form.setFieldValue('activities', recipe.activities || []);
form.setFieldValue('parameters', recipe.parameters || []);
if (recipe.response?.json_schema) {
form.setFieldValue(
'jsonSchema',
JSON.stringify(recipe.response.json_schema, null, 2)
);
}
} else {
console.error('No recipe in response:', response);
}
setHasAnalyzed(true);
})
.catch((error) => {
console.error('Failed to analyze messages:', error);
setAnalysisStage('Analysis failed');
})
.finally(() => {
clearInterval(stageInterval);
setHasAnalyzed(true);
setTimeout(() => {
setIsAnalyzing(false);
setAnalysisStage('');
}, 500); // Brief delay to show completion
});
}
}, [isOpen, sessionId, hasAnalyzed, form, intl]);
// Reset analysis state when modal closes
useEffect(() => {
if (!isOpen) {
setHasAnalyzed(false);
setIsAnalyzing(false);
setAnalysisStage('');
}
}, [isOpen]);
// Subscribe to form changes using the form's subscribe method
useEffect(() => {
const unsubscribe = form.store.subscribe(() => {
const hasTitle = form.state.values.title?.trim();
const hasDescription = form.state.values.description?.trim();
const hasInstructions = form.state.values.instructions?.trim();
const valid = !!(hasTitle && hasDescription && hasInstructions);
setIsFormValid(valid);
});
// Initial validation check
const hasTitle = form.state.values.title?.trim();
const hasDescription = form.state.values.description?.trim();
const hasInstructions = form.state.values.instructions?.trim();
const valid = !!(hasTitle && hasDescription && hasInstructions);
setIsFormValid(valid);
return unsubscribe;
}, [form]);
const handleCreateRecipe = async (formData: RecipeFormData, runAfterSave = false) => {
if (!isFormValid) {
return;
}
setIsCreating(true);
try {
const formattedSubRecipes =
formData.subRecipes.length > 0
? formData.subRecipes.map((subRecipe) => ({
name: subRecipe.name,
path: subRecipe.path,
description: subRecipe.description || undefined,
values:
subRecipe.values && Object.keys(subRecipe.values).length > 0
? subRecipe.values
: undefined,
sequential_when_repeated: subRecipe.sequential_when_repeated,
}))
: undefined;
const recipe: Recipe = {
title: formData.title,
description: formData.description,
instructions: formData.instructions,
prompt: formData.prompt || undefined,
activities: formData.activities.filter((activity) => activity.trim() !== ''),
parameters: formData.parameters.map((param) => ({
key: param.key,
input_type: param.input_type || 'string',
requirement: param.requirement,
description: param.description,
...(param.requirement === 'optional' && param.default ? { default: param.default } : {}),
...(param.input_type === 'select' && param.options
? {
options: param.options.filter((opt: string) => opt.trim() !== ''),
}
: {}),
})),
response:
formData.jsonSchema && formData.jsonSchema.trim()
? {
json_schema: JSON.parse(formData.jsonSchema),
}
: undefined,
sub_recipes: formattedSubRecipes,
};
const { id: recipeId } = await saveRecipe(recipe, null);
onRecipeCreated?.(recipe);
onClose();
if (runAfterSave) {
window.electron.createChatWindow({ recipeId });
}
} catch (error) {
console.error('Failed to create recipe:', error);
toastError({
title: intl.formatMessage(i18n.failedToCreateTitle),
msg: errorMessage(
error,
intl.formatMessage(i18n.failedToCreateDefaultMsg)
),
});
} finally {
setIsCreating(false);
}
};
if (!isOpen) return null;
return (
<div
className="fixed inset-0 z-[400] flex items-center justify-center bg-black/50 p-4"
data-testid="create-recipe-modal"
>
<div className="bg-background-primary border border-border-primary rounded-lg w-full max-w-4xl h-full max-h-[90vh] flex flex-col shadow-xl">
{/* Header */}
<div
className="flex items-center justify-between p-6 border-b border-border-primary shrink-0"
data-testid="modal-header"
>
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-background-primary rounded-full flex items-center justify-center">
<Geese className="w-6 h-6 text-iconProminent" />
</div>
<div>
<h1 className="text-xl font-medium text-text-primary">{intl.formatMessage(i18n.title)}</h1>
<p className="text-text-secondary text-sm">
{intl.formatMessage(i18n.subtitle)}
</p>
</div>
</div>
<Button
onClick={onClose}
variant="ghost"
size="sm"
className="p-2 hover:bg-background-secondary rounded-lg transition-colors"
data-testid="close-button"
>
<X className="w-5 h-5" />
</Button>
</div>
{/* Content */}
<div className="flex-1 overflow-y-auto px-6 py-4 min-h-0" data-testid="modal-content">
{isAnalyzing ? (
<div
className="flex flex-col items-center justify-center h-full min-h-[300px] space-y-4"
data-testid="analyzing-state"
>
<div className="flex items-center space-x-3">
<Loader2
className="w-6 h-6 animate-spin text-iconProminent"
data-testid="analysis-spinner"
/>
<div
className="text-lg font-medium text-text-primary"
data-testid="analyzing-title"
>
{intl.formatMessage(i18n.analyzingTitle)}
</div>
</div>
<div
className="text-text-secondary text-center max-w-md"
data-testid="analysis-stage"
>
{analysisStage}
</div>
<div className="flex items-center space-x-2 text-text-secondary">
<Geese className="w-5 h-5 animate-pulse" />
<span className="text-sm">{intl.formatMessage(i18n.extractingInsights)}</span>
</div>
</div>
) : (
<div data-testid="form-state">
<RecipeFormFields form={form} />
</div>
)}
</div>
{/* Footer */}
<div
className="flex items-center justify-between p-6 border-t border-border-primary shrink-0"
data-testid="modal-footer"
>
<Button
onClick={onClose}
variant="ghost"
className="px-4 py-2 text-text-secondary rounded-lg hover:bg-background-secondary transition-colors"
data-testid="cancel-button"
>
{intl.formatMessage(i18n.cancel)}
</Button>
<div className="flex gap-3">
{!isAnalyzing && (
<>
<Button
onClick={() => {
form.handleSubmit();
}}
disabled={!isFormValid || isCreating}
variant="outline"
className="px-4 py-2 border border-border-primary rounded-lg hover:bg-background-secondary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
data-testid="create-recipe-button"
>
<Save className="w-4 h-4 mr-2" />
{isCreating ? intl.formatMessage(i18n.creating) : intl.formatMessage(i18n.createRecipe)}
</Button>
<Button
onClick={() => {
handleCreateRecipe(form.state.values, true);
}}
disabled={!isFormValid || isCreating}
className="px-4 py-2 text-text-inverse rounded-lg hover:bg-opacity-90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
data-testid="create-and-run-recipe-button"
>
<Play className="w-4 h-4 mr-2" />
{isCreating ? intl.formatMessage(i18n.creating) : intl.formatMessage(i18n.createAndRunRecipe)}
</Button>
</>
)}
</div>
</div>
</div>
</div>
);
}
@@ -1,318 +0,0 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, type RenderOptions, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import CreateRecipeFromSessionModal from '../CreateRecipeFromSessionModal';
import { createRecipe } from '../../../api/sdk.gen';
import type { CreateRecipeResponse } from '../../../api/types.gen';
import { IntlTestWrapper } from '../../../i18n/test-utils';
const renderWithIntl = (ui: React.ReactElement, options?: RenderOptions) =>
render(ui, { wrapper: IntlTestWrapper, ...options });
vi.mock('../../../api/sdk.gen', () => ({
createRecipe: vi.fn(),
}));
vi.mock('../../../toasts', () => ({
toastError: vi.fn(),
}));
vi.mock('../../../recipe/recipe_management', () => ({
saveRecipe: vi.fn().mockResolvedValue({ id: 'mock-recipe-id', fileName: 'mock-recipe.yaml' }),
}));
vi.mock('../../ConfigContext', () => ({
useConfig: () => ({
extensionsList: [],
getExtensions: vi.fn().mockResolvedValue([]),
getProviders: vi.fn().mockResolvedValue([]),
}),
}));
const mockCreateRecipe = vi.mocked(createRecipe);
describe('CreateRecipeFromSessionModal', () => {
const defaultProps = {
isOpen: true,
onClose: vi.fn(),
sessionId: 'test-session-id',
onRecipeCreated: vi.fn(),
};
beforeEach(() => {
vi.clearAllMocks();
const mockResponse: CreateRecipeResponse = {
recipe: {
title: 'Analyzed Recipe Title',
description: 'Analyzed description',
instructions: 'Analyzed instructions with {{param1}}',
prompt: 'Analyzed prompt',
activities: ['activity1', 'activity2'],
parameters: [
{
key: 'param1',
description: 'Auto-detected parameter',
input_type: 'string',
requirement: 'required',
},
],
response: {
json_schema: { type: 'object' },
},
},
error: undefined,
};
mockCreateRecipe.mockResolvedValue({
data: mockResponse,
error: undefined,
request: new globalThis.Request('http://localhost/test'),
response: new globalThis.Response(),
});
});
describe('Modal Rendering', () => {
it('renders modal when open', () => {
renderWithIntl(<CreateRecipeFromSessionModal {...defaultProps} />);
expect(screen.getByTestId('create-recipe-modal')).toBeInTheDocument();
});
it('does not render when closed', () => {
renderWithIntl(<CreateRecipeFromSessionModal {...defaultProps} isOpen={false} />);
expect(screen.queryByTestId('create-recipe-modal')).not.toBeInTheDocument();
});
it('renders modal header with close button', () => {
renderWithIntl(<CreateRecipeFromSessionModal {...defaultProps} />);
expect(screen.getByTestId('modal-header')).toBeInTheDocument();
expect(screen.getByTestId('close-button')).toBeInTheDocument();
});
it('calls onClose when close button is clicked', async () => {
const user = userEvent.setup();
renderWithIntl(<CreateRecipeFromSessionModal {...defaultProps} />);
await user.click(screen.getByTestId('close-button'));
expect(defaultProps.onClose).toHaveBeenCalled();
});
});
describe('Analysis Workflow', () => {
it('shows analyzing state initially', () => {
renderWithIntl(<CreateRecipeFromSessionModal {...defaultProps} />);
expect(screen.getByTestId('analyzing-state')).toBeInTheDocument();
expect(screen.getByTestId('analyzing-title')).toBeInTheDocument();
});
it('displays analysis progress indicator', async () => {
renderWithIntl(<CreateRecipeFromSessionModal {...defaultProps} />);
expect(screen.getByTestId('analysis-stage')).toBeInTheDocument();
await waitFor(
() => {
const stageElement = screen.getByTestId('analysis-stage');
expect(stageElement).toBeInTheDocument();
},
{ timeout: 1000 }
);
});
it('shows loading indicator during analysis', () => {
renderWithIntl(<CreateRecipeFromSessionModal {...defaultProps} />);
expect(screen.getByTestId('analysis-spinner')).toBeInTheDocument();
});
it('transitions to form state after analysis completes', async () => {
renderWithIntl(<CreateRecipeFromSessionModal {...defaultProps} />);
await waitFor(
() => {
expect(screen.getByTestId('form-state')).toBeInTheDocument();
},
{ timeout: 3000 }
);
expect(screen.queryByTestId('analyzing-state')).not.toBeInTheDocument();
});
});
describe('Form Pre-filling', () => {
it('pre-fills form with analyzed data', async () => {
renderWithIntl(<CreateRecipeFromSessionModal {...defaultProps} />);
// Wait for analysis to complete and form to be pre-filled
await waitFor(
() => {
expect(screen.getByDisplayValue('Analyzed Recipe Title')).toBeInTheDocument();
},
{ timeout: 2000 }
);
expect(screen.getByDisplayValue('Analyzed description')).toBeInTheDocument();
expect(screen.getByDisplayValue('Analyzed instructions with {{param1}}')).toBeInTheDocument();
const promptInput = screen.getByTestId('prompt-input');
expect(promptInput).toBeInTheDocument();
});
it('shows recipe form fields after analysis', async () => {
renderWithIntl(<CreateRecipeFromSessionModal {...defaultProps} />);
await waitFor(
() => {
expect(screen.getByTestId('recipe-form')).toBeInTheDocument();
},
{ timeout: 2000 }
);
expect(screen.getByTestId('title-input')).toBeInTheDocument();
expect(screen.getByTestId('description-input')).toBeInTheDocument();
expect(screen.getByTestId('instructions-input')).toBeInTheDocument();
expect(screen.getByTestId('prompt-input')).toBeInTheDocument();
});
});
describe('Form Interactions', () => {
it('allows editing form fields', async () => {
const user = userEvent.setup();
renderWithIntl(<CreateRecipeFromSessionModal {...defaultProps} />);
await waitFor(
() => {
expect(screen.getByTestId('title-input')).toBeInTheDocument();
},
{ timeout: 2000 }
);
const titleInput = screen.getByTestId('title-input');
await user.clear(titleInput);
await user.type(titleInput, 'Modified Title');
expect(screen.getByDisplayValue('Modified Title')).toBeInTheDocument();
});
it('validates required fields', async () => {
const user = userEvent.setup();
renderWithIntl(<CreateRecipeFromSessionModal {...defaultProps} />);
await waitFor(
() => {
expect(screen.getByTestId('create-recipe-button')).toBeInTheDocument();
},
{ timeout: 2000 }
);
const titleInput = screen.getByTestId('title-input');
await user.clear(titleInput);
const createButton = screen.getByTestId('create-recipe-button');
expect(createButton).toBeDisabled();
});
});
describe('Recipe Creation', () => {
it('enables create button when form is valid', async () => {
renderWithIntl(<CreateRecipeFromSessionModal {...defaultProps} />);
await waitFor(
() => {
const createButton = screen.getByTestId('create-recipe-button');
expect(createButton).toBeEnabled();
},
{ timeout: 2000 }
);
});
it('creates recipe and closes modal when form is submitted', async () => {
const user = userEvent.setup();
renderWithIntl(<CreateRecipeFromSessionModal {...defaultProps} />);
await waitFor(
() => {
expect(screen.getByTestId('create-recipe-button')).toBeEnabled();
},
{ timeout: 2000 }
);
await user.click(screen.getByTestId('create-recipe-button'));
await waitFor(() => {
expect(defaultProps.onRecipeCreated).toHaveBeenCalled();
expect(defaultProps.onClose).toHaveBeenCalled();
});
});
});
describe('Modal Footer', () => {
it('shows cancel button in all states', async () => {
renderWithIntl(<CreateRecipeFromSessionModal {...defaultProps} />);
expect(screen.getByTestId('cancel-button')).toBeInTheDocument();
await waitFor(
() => {
expect(screen.getByTestId('create-recipe-button')).toBeInTheDocument();
},
{ timeout: 2000 }
);
expect(screen.getByTestId('cancel-button')).toBeInTheDocument();
});
it('calls onClose when cancel button is clicked', async () => {
const user = userEvent.setup();
renderWithIntl(<CreateRecipeFromSessionModal {...defaultProps} />);
await user.click(screen.getByTestId('cancel-button'));
expect(defaultProps.onClose).toHaveBeenCalled();
});
it('shows different button states based on workflow stage', async () => {
renderWithIntl(<CreateRecipeFromSessionModal {...defaultProps} />);
expect(screen.getByTestId('cancel-button')).toBeInTheDocument();
expect(screen.queryByTestId('create-recipe-button')).not.toBeInTheDocument();
await waitFor(
() => {
expect(screen.getByTestId('create-recipe-button')).toBeInTheDocument();
},
{ timeout: 2000 }
);
expect(screen.getByTestId('create-and-run-recipe-button')).toBeInTheDocument();
});
});
describe('Error Handling', () => {
it('handles analysis errors gracefully', async () => {
renderWithIntl(<CreateRecipeFromSessionModal {...defaultProps} sessionId="" />);
expect(screen.getByTestId('create-recipe-modal')).toBeInTheDocument();
});
it('handles form validation errors', async () => {
const user = userEvent.setup();
renderWithIntl(<CreateRecipeFromSessionModal {...defaultProps} />);
await waitFor(
() => {
expect(screen.getByTestId('title-input')).toBeInTheDocument();
},
{ timeout: 2000 }
);
await user.clear(screen.getByTestId('title-input'));
await user.clear(screen.getByTestId('description-input'));
await user.clear(screen.getByTestId('instructions-input'));
const createButton = screen.getByTestId('create-recipe-button');
expect(createButton).toBeDisabled();
});
});
});
-60
View File
@@ -116,15 +116,6 @@
"baseChat.goHome": {
"defaultMessage": "Go home"
},
"baseChat.noSession": {
"defaultMessage": "No Session"
},
"baseChat.recipeCreatedMessage": {
"defaultMessage": "\"{title}\" has been saved and is ready to use."
},
"baseChat.recipeCreatedTitle": {
"defaultMessage": "Recipe created successfully!"
},
"bottomMenuExtensionSelection.extensionUpdated": {
"defaultMessage": "Extension Updated"
},
@@ -167,9 +158,6 @@
"chatInput.contextWindow": {
"defaultMessage": "Context window"
},
"chatInput.createRecipeFromSession": {
"defaultMessage": "Create Recipe from Session"
},
"chatInput.dictationError": {
"defaultMessage": "Dictation Error"
},
@@ -401,54 +389,6 @@
"createEditRecipe.viewEditRecipeTitle": {
"defaultMessage": "View/edit recipe"
},
"createRecipeFromSession.analyzingTitle": {
"defaultMessage": "Analyzing your conversation"
},
"createRecipeFromSession.cancel": {
"defaultMessage": "Cancel"
},
"createRecipeFromSession.createAndRunRecipe": {
"defaultMessage": "Create & Run Recipe"
},
"createRecipeFromSession.createRecipe": {
"defaultMessage": "Create Recipe"
},
"createRecipeFromSession.creating": {
"defaultMessage": "Creating..."
},
"createRecipeFromSession.extractingInsights": {
"defaultMessage": "Extracting insights from your chat"
},
"createRecipeFromSession.failedToCreateDefaultMsg": {
"defaultMessage": "An unexpected error occurred while creating the recipe. Please try again."
},
"createRecipeFromSession.failedToCreateTitle": {
"defaultMessage": "Failed to create recipe"
},
"createRecipeFromSession.stageComplete": {
"defaultMessage": "Complete!"
},
"createRecipeFromSession.stageExtracting": {
"defaultMessage": "Extracting main topics..."
},
"createRecipeFromSession.stageFinalizing": {
"defaultMessage": "Finalizing details..."
},
"createRecipeFromSession.stageGenerating": {
"defaultMessage": "Generating recipe structure..."
},
"createRecipeFromSession.stageIdentifying": {
"defaultMessage": "Identifying key patterns..."
},
"createRecipeFromSession.stageReading": {
"defaultMessage": "Reading your conversation..."
},
"createRecipeFromSession.subtitle": {
"defaultMessage": "Create a reusable recipe based on your current conversation."
},
"createRecipeFromSession.title": {
"defaultMessage": "Create Recipe from Session"
},
"createSubRecipeInline.cancel": {
"defaultMessage": "Cancel"
},
-60
View File
@@ -116,15 +116,6 @@
"baseChat.goHome": {
"defaultMessage": "Ir al inicio"
},
"baseChat.noSession": {
"defaultMessage": "Sin sesión"
},
"baseChat.recipeCreatedMessage": {
"defaultMessage": "\"{title}\" se guardó y está lista para usar."
},
"baseChat.recipeCreatedTitle": {
"defaultMessage": "¡Receta creada con éxito!"
},
"bottomMenuExtensionSelection.extensionUpdated": {
"defaultMessage": "Extensión actualizada"
},
@@ -167,9 +158,6 @@
"chatInput.contextWindow": {
"defaultMessage": "Ventana de contexto"
},
"chatInput.createRecipeFromSession": {
"defaultMessage": "Crear Receta desde la sesión"
},
"chatInput.dictationError": {
"defaultMessage": "Error de dictado"
},
@@ -401,54 +389,6 @@
"createEditRecipe.viewEditRecipeTitle": {
"defaultMessage": "Ver/editar receta"
},
"createRecipeFromSession.analyzingTitle": {
"defaultMessage": "Analizando tu conversación"
},
"createRecipeFromSession.cancel": {
"defaultMessage": "Cancelar"
},
"createRecipeFromSession.createAndRunRecipe": {
"defaultMessage": "Crear y ejecutar Receta"
},
"createRecipeFromSession.createRecipe": {
"defaultMessage": "Crear Receta"
},
"createRecipeFromSession.creating": {
"defaultMessage": "Creando..."
},
"createRecipeFromSession.extractingInsights": {
"defaultMessage": "Extrayendo ideas de tu chat"
},
"createRecipeFromSession.failedToCreateDefaultMsg": {
"defaultMessage": "Ocurrió un error inesperado al crear la receta. Inténtalo de nuevo."
},
"createRecipeFromSession.failedToCreateTitle": {
"defaultMessage": "No se pudo crear la receta"
},
"createRecipeFromSession.stageComplete": {
"defaultMessage": "¡Completado!"
},
"createRecipeFromSession.stageExtracting": {
"defaultMessage": "Extrayendo los temas principales..."
},
"createRecipeFromSession.stageFinalizing": {
"defaultMessage": "Finalizando los detalles..."
},
"createRecipeFromSession.stageGenerating": {
"defaultMessage": "Generando la estructura de la receta..."
},
"createRecipeFromSession.stageIdentifying": {
"defaultMessage": "Identificando patrones clave..."
},
"createRecipeFromSession.stageReading": {
"defaultMessage": "Leyendo tu conversación..."
},
"createRecipeFromSession.subtitle": {
"defaultMessage": "Crea una receta reutilizable a partir de tu conversación actual."
},
"createRecipeFromSession.title": {
"defaultMessage": "Crear Receta desde la sesión"
},
"createSubRecipeInline.cancel": {
"defaultMessage": "Cancelar"
},
-60
View File
@@ -116,15 +116,6 @@
"baseChat.goHome": {
"defaultMessage": "घर जाओ"
},
"baseChat.noSession": {
"defaultMessage": "कोई सत्र नहीं"
},
"baseChat.recipeCreatedMessage": {
"defaultMessage": "\"{title}\" सहेजा गया है और उपयोग के लिए तैयार है।"
},
"baseChat.recipeCreatedTitle": {
"defaultMessage": "रेसिपी सफलतापूर्वक बनाई गई!"
},
"bottomMenuExtensionSelection.extensionUpdated": {
"defaultMessage": "एक्सटेंशन अपडेट किया गया"
},
@@ -167,9 +158,6 @@
"chatInput.contextWindow": {
"defaultMessage": "प्रसंग विंडो"
},
"chatInput.createRecipeFromSession": {
"defaultMessage": "सत्र से रेसिपी बनाएं"
},
"chatInput.dictationError": {
"defaultMessage": "श्रुतलेख त्रुटि"
},
@@ -401,54 +389,6 @@
"createEditRecipe.viewEditRecipeTitle": {
"defaultMessage": "रेसिपी देखें/संपादित करें"
},
"createRecipeFromSession.analyzingTitle": {
"defaultMessage": "आपकी बातचीत का विश्लेषण"
},
"createRecipeFromSession.cancel": {
"defaultMessage": "रद्द करें"
},
"createRecipeFromSession.createAndRunRecipe": {
"defaultMessage": "रेसिपी बनाएं और चलाएं"
},
"createRecipeFromSession.createRecipe": {
"defaultMessage": "रेसिपी बनाएं"
},
"createRecipeFromSession.creating": {
"defaultMessage": "बनाया जा रहा है..."
},
"createRecipeFromSession.extractingInsights": {
"defaultMessage": "आपकी चैट से अंतर्दृष्टि निकालना"
},
"createRecipeFromSession.failedToCreateDefaultMsg": {
"defaultMessage": "रेसिपी बनाते समय एक अप्रत्याशित त्रुटि उत्पन्न हुई. कृपया पुन: प्रयास करें।"
},
"createRecipeFromSession.failedToCreateTitle": {
"defaultMessage": "रेसिपी बनाने में विफल"
},
"createRecipeFromSession.stageComplete": {
"defaultMessage": "पूरा!"
},
"createRecipeFromSession.stageExtracting": {
"defaultMessage": "मुख्य विषय निकाले जा रहे हैं..."
},
"createRecipeFromSession.stageFinalizing": {
"defaultMessage": "विवरण को अंतिम रूप दिया जा रहा है..."
},
"createRecipeFromSession.stageGenerating": {
"defaultMessage": "रेसिपी संरचना तैयार की जा रही है..."
},
"createRecipeFromSession.stageIdentifying": {
"defaultMessage": "प्रमुख पैटर्न की पहचान..."
},
"createRecipeFromSession.stageReading": {
"defaultMessage": "आपकी बातचीत पढ़ रहा हूँ..."
},
"createRecipeFromSession.subtitle": {
"defaultMessage": "अपनी मौजूदा बातचीत के आधार पर दोबारा इस्तेमाल होने वाली रेसिपी बनाएं।"
},
"createRecipeFromSession.title": {
"defaultMessage": "सत्र से रेसिपी बनाएं"
},
"createSubRecipeInline.cancel": {
"defaultMessage": "रद्द करें"
},
-60
View File
@@ -116,15 +116,6 @@
"baseChat.goHome": {
"defaultMessage": "ホームへ"
},
"baseChat.noSession": {
"defaultMessage": "セッションなし"
},
"baseChat.recipeCreatedMessage": {
"defaultMessage": "\"{title}\" を保存しました。使用できます。"
},
"baseChat.recipeCreatedTitle": {
"defaultMessage": "レシピを作成しました!"
},
"bottomMenuExtensionSelection.extensionUpdated": {
"defaultMessage": "拡張機能を更新しました"
},
@@ -167,9 +158,6 @@
"chatInput.contextWindow": {
"defaultMessage": "コンテキストウィンドウ"
},
"chatInput.createRecipeFromSession": {
"defaultMessage": "セッションからレシピを作成"
},
"chatInput.dictationError": {
"defaultMessage": "音声入力エラー"
},
@@ -401,54 +389,6 @@
"createEditRecipe.viewEditRecipeTitle": {
"defaultMessage": "レシピを表示/編集"
},
"createRecipeFromSession.analyzingTitle": {
"defaultMessage": "会話を分析中"
},
"createRecipeFromSession.cancel": {
"defaultMessage": "キャンセル"
},
"createRecipeFromSession.createAndRunRecipe": {
"defaultMessage": "レシピを作成して実行"
},
"createRecipeFromSession.createRecipe": {
"defaultMessage": "レシピを作成"
},
"createRecipeFromSession.creating": {
"defaultMessage": "作成中..."
},
"createRecipeFromSession.extractingInsights": {
"defaultMessage": "チャットからインサイトを抽出中"
},
"createRecipeFromSession.failedToCreateDefaultMsg": {
"defaultMessage": "レシピの作成中に予期しないエラーが発生しました。もう一度お試しください。"
},
"createRecipeFromSession.failedToCreateTitle": {
"defaultMessage": "レシピの作成に失敗しました"
},
"createRecipeFromSession.stageComplete": {
"defaultMessage": "完了!"
},
"createRecipeFromSession.stageExtracting": {
"defaultMessage": "主要トピックを抽出中..."
},
"createRecipeFromSession.stageFinalizing": {
"defaultMessage": "詳細を仕上げ中..."
},
"createRecipeFromSession.stageGenerating": {
"defaultMessage": "レシピ構造を生成中..."
},
"createRecipeFromSession.stageIdentifying": {
"defaultMessage": "主要なパターンを特定中..."
},
"createRecipeFromSession.stageReading": {
"defaultMessage": "会話を読み込み中..."
},
"createRecipeFromSession.subtitle": {
"defaultMessage": "現在の会話に基づいて、再利用可能なレシピを作成します。"
},
"createRecipeFromSession.title": {
"defaultMessage": "セッションからレシピを作成"
},
"createSubRecipeInline.cancel": {
"defaultMessage": "キャンセル"
},
-60
View File
@@ -116,15 +116,6 @@
"baseChat.goHome": {
"defaultMessage": "홈으로 이동"
},
"baseChat.noSession": {
"defaultMessage": "세션 없음"
},
"baseChat.recipeCreatedMessage": {
"defaultMessage": "\"{title}\"이 저장되어 사용할 수 있습니다."
},
"baseChat.recipeCreatedTitle": {
"defaultMessage": "레시피가 성공적으로 생성되었습니다!"
},
"bottomMenuExtensionSelection.extensionUpdated": {
"defaultMessage": "익스텐션이 업데이트되었습니다."
},
@@ -167,9 +158,6 @@
"chatInput.contextWindow": {
"defaultMessage": "컨텍스트 창"
},
"chatInput.createRecipeFromSession": {
"defaultMessage": "세션에서 레시피 생성"
},
"chatInput.dictationError": {
"defaultMessage": "받아쓰기 오류"
},
@@ -401,54 +389,6 @@
"createEditRecipe.viewEditRecipeTitle": {
"defaultMessage": "레시피 보기/수정"
},
"createRecipeFromSession.analyzingTitle": {
"defaultMessage": "대화 분석 중"
},
"createRecipeFromSession.cancel": {
"defaultMessage": "취소"
},
"createRecipeFromSession.createAndRunRecipe": {
"defaultMessage": "레시피 생성 및 실행"
},
"createRecipeFromSession.createRecipe": {
"defaultMessage": "레시피 생성"
},
"createRecipeFromSession.creating": {
"defaultMessage": "생성 중..."
},
"createRecipeFromSession.extractingInsights": {
"defaultMessage": "채팅에서 통찰력 추출"
},
"createRecipeFromSession.failedToCreateDefaultMsg": {
"defaultMessage": "레시피를 생성하는 중에 예상치 못한 오류가 발생했습니다. 다시 시도해 주세요."
},
"createRecipeFromSession.failedToCreateTitle": {
"defaultMessage": "레시피를 생성하지 못했습니다."
},
"createRecipeFromSession.stageComplete": {
"defaultMessage": "완료!"
},
"createRecipeFromSession.stageExtracting": {
"defaultMessage": "주요 주제 추출 중..."
},
"createRecipeFromSession.stageFinalizing": {
"defaultMessage": "세부정보를 마무리하는 중..."
},
"createRecipeFromSession.stageGenerating": {
"defaultMessage": "레시피 구조 생성 중..."
},
"createRecipeFromSession.stageIdentifying": {
"defaultMessage": "주요 패턴 식별 중..."
},
"createRecipeFromSession.stageReading": {
"defaultMessage": "대화를 읽는 중..."
},
"createRecipeFromSession.subtitle": {
"defaultMessage": "현재 대화를 기반으로 재사용 가능한 레시피를 만드세요."
},
"createRecipeFromSession.title": {
"defaultMessage": "세션에서 레시피 생성"
},
"createSubRecipeInline.cancel": {
"defaultMessage": "취소"
},
-60
View File
@@ -116,15 +116,6 @@
"baseChat.goHome": {
"defaultMessage": "На главную"
},
"baseChat.noSession": {
"defaultMessage": "Нет сеанса"
},
"baseChat.recipeCreatedMessage": {
"defaultMessage": "«{title}» сохранено и готово к использованию."
},
"baseChat.recipeCreatedTitle": {
"defaultMessage": "Рецепт успешно создан!"
},
"bottomMenuExtensionSelection.extensionUpdated": {
"defaultMessage": "Расширение обновлено"
},
@@ -167,9 +158,6 @@
"chatInput.contextWindow": {
"defaultMessage": "Контекстное окно"
},
"chatInput.createRecipeFromSession": {
"defaultMessage": "Создать рецепт из сеанса"
},
"chatInput.dictationError": {
"defaultMessage": "Ошибка диктовки"
},
@@ -401,54 +389,6 @@
"createEditRecipe.viewEditRecipeTitle": {
"defaultMessage": "Просмотреть/изменить рецепт"
},
"createRecipeFromSession.analyzingTitle": {
"defaultMessage": "Анализ вашего разговора"
},
"createRecipeFromSession.cancel": {
"defaultMessage": "Отмена"
},
"createRecipeFromSession.createAndRunRecipe": {
"defaultMessage": "Создать и запустить рецепт"
},
"createRecipeFromSession.createRecipe": {
"defaultMessage": "Создать рецепт"
},
"createRecipeFromSession.creating": {
"defaultMessage": "Создание..."
},
"createRecipeFromSession.extractingInsights": {
"defaultMessage": "Извлечение сведений из вашего чата"
},
"createRecipeFromSession.failedToCreateDefaultMsg": {
"defaultMessage": "При создании рецепта произошла непредвиденная ошибка. Повторите попытку."
},
"createRecipeFromSession.failedToCreateTitle": {
"defaultMessage": "Не удалось создать рецепт"
},
"createRecipeFromSession.stageComplete": {
"defaultMessage": "Готово!"
},
"createRecipeFromSession.stageExtracting": {
"defaultMessage": "Извлечение основных тем..."
},
"createRecipeFromSession.stageFinalizing": {
"defaultMessage": "Завершение деталей..."
},
"createRecipeFromSession.stageGenerating": {
"defaultMessage": "Создание структуры рецепта..."
},
"createRecipeFromSession.stageIdentifying": {
"defaultMessage": "Определение ключевых шаблонов..."
},
"createRecipeFromSession.stageReading": {
"defaultMessage": "Чтение вашего разговора..."
},
"createRecipeFromSession.subtitle": {
"defaultMessage": "Создайте повторно используемый рецепт на основе текущего разговора."
},
"createRecipeFromSession.title": {
"defaultMessage": "Создать рецепт из сеанса"
},
"createSubRecipeInline.cancel": {
"defaultMessage": "Отмена"
},
-60
View File
@@ -116,15 +116,6 @@
"baseChat.goHome": {
"defaultMessage": "Ana sayfaya git"
},
"baseChat.noSession": {
"defaultMessage": "Oturum Yok"
},
"baseChat.recipeCreatedMessage": {
"defaultMessage": "\"{title}\" kaydedildi ve kullanıma hazır."
},
"baseChat.recipeCreatedTitle": {
"defaultMessage": "Tarif başarıyla oluşturuldu!"
},
"bottomMenuExtensionSelection.extensionUpdated": {
"defaultMessage": "Uzantı Güncellendi"
},
@@ -167,9 +158,6 @@
"chatInput.contextWindow": {
"defaultMessage": "Bağlam penceresi"
},
"chatInput.createRecipeFromSession": {
"defaultMessage": "Oturumdan Tarif Oluştur"
},
"chatInput.dictationError": {
"defaultMessage": "Dikte Hatası"
},
@@ -401,54 +389,6 @@
"createEditRecipe.viewEditRecipeTitle": {
"defaultMessage": "Tarifi görüntüle/düzenle"
},
"createRecipeFromSession.analyzingTitle": {
"defaultMessage": "Konuşmanızı analiz etme"
},
"createRecipeFromSession.cancel": {
"defaultMessage": "İptal"
},
"createRecipeFromSession.createAndRunRecipe": {
"defaultMessage": "Tarif Oluştur ve Çalıştır"
},
"createRecipeFromSession.createRecipe": {
"defaultMessage": "Tarif Oluştur"
},
"createRecipeFromSession.creating": {
"defaultMessage": "Oluşturuluyor..."
},
"createRecipeFromSession.extractingInsights": {
"defaultMessage": "Sohbetinizden içgörüler çıkarma"
},
"createRecipeFromSession.failedToCreateDefaultMsg": {
"defaultMessage": "Tarif oluşturulurken beklenmeyen bir hata oluştu. Lütfen tekrar deneyin."
},
"createRecipeFromSession.failedToCreateTitle": {
"defaultMessage": "Tarif oluşturulamadı"
},
"createRecipeFromSession.stageComplete": {
"defaultMessage": "Tamamla!"
},
"createRecipeFromSession.stageExtracting": {
"defaultMessage": "Ana konular çıkarılıyor..."
},
"createRecipeFromSession.stageFinalizing": {
"defaultMessage": "Ayrıntılar tamamlanıyor..."
},
"createRecipeFromSession.stageGenerating": {
"defaultMessage": "Tarif yapısı oluşturuluyor..."
},
"createRecipeFromSession.stageIdentifying": {
"defaultMessage": "Anahtar kalıpları belirlemek..."
},
"createRecipeFromSession.stageReading": {
"defaultMessage": "Konuşmanızı okuyorum..."
},
"createRecipeFromSession.subtitle": {
"defaultMessage": "Mevcut konuşmanıza göre yeniden kullanılabilir bir tarif oluşturun."
},
"createRecipeFromSession.title": {
"defaultMessage": "Oturumdan Tarif Oluştur"
},
"createSubRecipeInline.cancel": {
"defaultMessage": "İptal"
},
-60
View File
@@ -116,15 +116,6 @@
"baseChat.goHome": {
"defaultMessage": "回到首页"
},
"baseChat.noSession": {
"defaultMessage": "无会话"
},
"baseChat.recipeCreatedMessage": {
"defaultMessage": "“{title}”已保存,可以使用了。"
},
"baseChat.recipeCreatedTitle": {
"defaultMessage": "配方创建成功!"
},
"bottomMenuExtensionSelection.extensionUpdated": {
"defaultMessage": "扩展已更新"
},
@@ -167,9 +158,6 @@
"chatInput.contextWindow": {
"defaultMessage": "上下文窗口"
},
"chatInput.createRecipeFromSession": {
"defaultMessage": "从会话创建配方"
},
"chatInput.dictationError": {
"defaultMessage": "语音输入错误"
},
@@ -401,54 +389,6 @@
"createEditRecipe.viewEditRecipeTitle": {
"defaultMessage": "查看/编辑配方"
},
"createRecipeFromSession.analyzingTitle": {
"defaultMessage": "正在分析你的对话"
},
"createRecipeFromSession.cancel": {
"defaultMessage": "取消"
},
"createRecipeFromSession.createAndRunRecipe": {
"defaultMessage": "创建并运行配方"
},
"createRecipeFromSession.createRecipe": {
"defaultMessage": "创建配方"
},
"createRecipeFromSession.creating": {
"defaultMessage": "创建中…"
},
"createRecipeFromSession.extractingInsights": {
"defaultMessage": "正在从你的聊天中提取要点"
},
"createRecipeFromSession.failedToCreateDefaultMsg": {
"defaultMessage": "创建配方时发生意外错误,请重试。"
},
"createRecipeFromSession.failedToCreateTitle": {
"defaultMessage": "创建配方失败"
},
"createRecipeFromSession.stageComplete": {
"defaultMessage": "完成!"
},
"createRecipeFromSession.stageExtracting": {
"defaultMessage": "正在提取主要话题…"
},
"createRecipeFromSession.stageFinalizing": {
"defaultMessage": "正在敲定细节…"
},
"createRecipeFromSession.stageGenerating": {
"defaultMessage": "正在生成配方结构…"
},
"createRecipeFromSession.stageIdentifying": {
"defaultMessage": "正在识别关键模式…"
},
"createRecipeFromSession.stageReading": {
"defaultMessage": "正在阅读你的对话…"
},
"createRecipeFromSession.subtitle": {
"defaultMessage": "基于当前对话创建一份可复用的配方。"
},
"createRecipeFromSession.title": {
"defaultMessage": "从会话创建配方"
},
"createSubRecipeInline.cancel": {
"defaultMessage": "取消"
},