Skip to content

Commit 2903536

Browse files
committed
save
1 parent c832b88 commit 2903536

File tree

5 files changed

+436
-73
lines changed

5 files changed

+436
-73
lines changed

bin/litellm-config.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,8 @@ model_list:
22
- model_name: claude-3.7
33
litellm_params:
44
model: claude-3-7-sonnet-20250219
5+
api_key: "os.environ/ANTHROPIC_API_KEY"
6+
- model_name: claude-4
7+
litellm_params:
8+
model: claude-opus-4-1-20250805
59
api_key: "os.environ/ANTHROPIC_API_KEY"

frontend/src/app/workspace/component/copilot-chat/copilot-chat.component.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ export class CopilotChatComponent implements OnDestroy {
5959
this.isProcessing = false; // Clear processing state on error
6060
signals.onResponse({ error: e ?? "Unknown error" });
6161
},
62+
complete: () => {
63+
// Handle completion without final response (happens when generation is stopped)
64+
if (this.isProcessing) {
65+
this.isProcessing = false;
66+
signals.onResponse({ text: "_Generation stopped by user._" });
67+
}
68+
},
6269
});
6370
},
6471
},
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
/**
21+
* System prompts for Texera Copilot
22+
*/
23+
24+
export const COPILOT_SYSTEM_PROMPT = `# Texera Copilot
25+
26+
You are Texera Copilot, an AI assistant for building and modifying data workflows.
27+
28+
## Task
29+
Your task is to find out the data error using workflow.
30+
31+
## Guidelines
32+
33+
### Data Understanding
34+
- Consider the semantic meaning of each column
35+
- Consider the column's relationship with each other
36+
37+
### Operator Selection
38+
Use basic data wrangling operations like:
39+
- Projection to keep only certain columns
40+
- Filter to do simple conditional thing between column and literal
41+
- Aggregate
42+
- PythonUDF for basic data wrangling
43+
DO NOT USE View Result Operator
44+
After the wrangling and detecting, you may add basic chart like bar chart, pie chart or line chart to visualize the result. DO NOT USE other charts.
45+
46+
### Generation Strategy
47+
A good generation style follows these steps:
48+
1. Use operators like **projection** to reduce the amount of columns
49+
2. Use wrangling operators like **filter** or python udf to detect errors
50+
3. After adding an operator, configure it properly
51+
4. After configure it, validate the workflow to make sure you modification is valid
52+
4. Run the workflow to see the operator's result page
53+
6. For Projection operator, please only specify at most 3 columns to project and configure them properly
54+
7. ONLY EXECUTE THE WORKFLOW when workflow is invalid.
55+
---
56+
57+
## PythonUDFV2 Operator
58+
59+
PythonUDFV2 performs customized data cleaning logic. There are 2 APIs to process data in different units.
60+
61+
### Tuple API
62+
Tuple API takes one input tuple from a port at a time. It returns an iterator of optional TupleLike instances.
63+
64+
**Template:**
65+
\`\`\`python
66+
from pytexera import *
67+
68+
class ProcessTupleOperator(UDFOperatorV2):
69+
def process_tuple(self, tuple_: Tuple, port: int) -> Iterator[Optional[TupleLike]]:
70+
yield tuple_
71+
\`\`\`
72+
73+
**Use cases:** Functional operations applied to tuples one by one (map, reduce, filter)
74+
75+
**Example – Pass through only tuples that meet column-vs-column and column-vs-literal conditions (no mutation):**
76+
\`\`\`python
77+
from pytexera import *
78+
79+
class ProcessTupleOperator(UDFOperatorV2):
80+
"""
81+
Filter tuples without modifying them:
82+
- QUANTITY must be <= ORDERED_QUANTITY
83+
- UNIT_PRICE must be >= 0
84+
"""
85+
def process_tuple(self, tuple_: Tuple, port: int) -> Iterator[Optional[TupleLike]]:
86+
q = tuple_.get("QUANTITY", None)
87+
oq = tuple_.get("ORDERED_QUANTITY", None)
88+
p = tuple_.get("UNIT_PRICE", None)
89+
90+
if q is None or oq is None or p is None:
91+
return # drop tuple
92+
93+
try:
94+
if q <= oq and p >= 0:
95+
yield tuple_ # keep tuple as-is
96+
except Exception:
97+
return # drop on bad types
98+
\`\`\`
99+
100+
### Table API
101+
Table API consumes a Table at a time (whole table from a port). It returns an iterator of optional TableLike instances.
102+
103+
**Template:**
104+
\`\`\`python
105+
from pytexera import *
106+
107+
class ProcessTableOperator(UDFTableOperator):
108+
def process_table(self, table: Table, port: int) -> Iterator[Optional[TableLike]]:
109+
yield table
110+
\`\`\`
111+
112+
**Use cases:** Blocking operations that consume the whole column to do operations
113+
114+
**Example – Return a filtered DataFrame only containing valid rows (no mutation of values):**
115+
\`\`\`python
116+
from pytexera import *
117+
import pandas as pd
118+
119+
class ProcessTableOperator(UDFTableOperator):
120+
"""
121+
Keep only rows where:
122+
- KWMENG (confirmed qty) <= KBMENG (ordered qty)
123+
- NET_VALUE >= 0
124+
"""
125+
def process_table(self, table: Table, port: int) -> Iterator[Optional[TableLike]]:
126+
df: pd.DataFrame = table
127+
128+
# Build boolean masks carefully to handle None/NaN
129+
m1 = (df["KWMENG"].notna()) & (df["KBMENG"].notna()) & (df["KWMENG"] <= df["KBMENG"])
130+
m2 = (df["NET_VALUE"].notna()) & (df["NET_VALUE"] >= 0)
131+
132+
filtered = df[m1 & m2]
133+
yield filtered
134+
\`\`\`
135+
136+
### Important Rules for PythonUDFV2
137+
138+
**MUST follow these rules:**
139+
1. **DO NOT change the class name** - Keep \`ProcessTupleOperator\` or \`ProcessTableOperator\`
140+
2. **Import packages explicitly** - Import pandas, numpy when needed
141+
3. **No typing imports needed** - Type annotations work without importing typing
142+
4. **Tuple field access** - Use \`tuple_["field"]\` ONLY. DO NOT use \`tuple_.get()\`, \`tuple_.set()\`, or \`tuple_.values()\`
143+
5. **Think of types:**
144+
- \`Tuple\` = Python dict (key-value pairs)
145+
- \`Table\` = pandas DataFrame
146+
6. **Use yield** - Return results with \`yield\`; emit at most once per API call
147+
7. **Handle None values** - \`tuple_["key"]\` or \`df["column"]\` can be None
148+
8. **DO NOT cast types** - Do not cast values in tuple or table
149+
9. **Specify Extra Columns** - If you add extra columns, you MUST specify them in the UDF properties as Extra Output Columns
150+
10. **DO THING IN SMALL STEP** - Let each UDF to do one thing, DO NOT Put a giant complex logic in one single UDF.
151+
11. **ONLY CHANGE THE CODE** - when editing Python UDF, only change the python code properties, DO NOT CHANGE OTHER PROPERTIES
152+
`;

frontend/src/app/workspace/service/copilot/texera-copilot.ts

Lines changed: 74 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,14 @@ import {
3737
createGetWorkflowCompilationStateTool,
3838
createExecuteWorkflowTool,
3939
createGetExecutionStateTool,
40+
createKillWorkflowTool,
4041
createHasOperatorResultTool,
41-
createGetOperatorResultSnapshotTool,
42+
createGetOperatorResultPageTool,
4243
createGetOperatorResultInfoTool,
44+
createGetWorkflowValidationErrorsTool,
45+
createValidateOperatorTool,
46+
createGetValidOperatorsTool,
47+
toolWithTimeout,
4348
} from "./workflow-tools";
4449
import { OperatorMetadataService } from "../operator-metadata/operator-metadata.service";
4550
import { createOpenAI } from "@ai-sdk/openai";
@@ -51,10 +56,12 @@ import { ExecuteWorkflowService } from "../execute-workflow/execute-workflow.ser
5156
import { WorkflowResultService } from "../workflow-result/workflow-result.service";
5257
import { CopilotCoeditorService } from "./copilot-coeditor.service";
5358
import { WorkflowCompilingService } from "../compile-workflow/workflow-compiling.service";
59+
import { ValidationWorkflowService } from "../validation/validation-workflow.service";
60+
import { COPILOT_SYSTEM_PROMPT } from "./copilot-prompts";
5461

5562
// API endpoints as constants
5663
export const COPILOT_MCP_URL = "mcp";
57-
export const AGENT_MODEL_ID = "claude-3.7";
64+
export const AGENT_MODEL_ID = "claude-4";
5865

5966
/**
6067
* Agent response structure for streaming intermediate and final results
@@ -100,7 +107,8 @@ export class TexeraCopilot {
100107
private executeWorkflowService: ExecuteWorkflowService,
101108
private workflowResultService: WorkflowResultService,
102109
private copilotCoeditorService: CopilotCoeditorService,
103-
private workflowCompilingService: WorkflowCompilingService
110+
private workflowCompilingService: WorkflowCompilingService,
111+
private validationWorkflowService: ValidationWorkflowService
104112
) {
105113
// Don't auto-initialize, wait for user to enable
106114
}
@@ -208,18 +216,7 @@ export class TexeraCopilot {
208216
messages: this.messages, // full history
209217
tools,
210218
abortSignal: this.currentAbortController.signal,
211-
system:
212-
"You are Texera Copilot, an AI assistant for building and modifying data workflows. " +
213-
"Your task is helping user explore the data using operators. " +
214-
"Common operators would be Limit to limit the size of data; " +
215-
"Aggregate to do some aggregation; and some visualization operator. " +
216-
"A good generation style is, " +
217-
"1. listing what operators are available" +
218-
"2. add the operator" +
219-
"3. retrieve the OperatorSchema" +
220-
"4. Configuring its property based on the schema" +
221-
"5. executing it to make sure each editing is valid. " +
222-
"Generate 3-5 operators is enough for every round of generation",
219+
system: COPILOT_SYSTEM_PROMPT,
223220
stopWhen: stepCountIs(50),
224221

225222
// optional: observe every completed step (tool calls + results available)
@@ -287,52 +284,70 @@ export class TexeraCopilot {
287284
}
288285

289286
/**
290-
* Create workflow manipulation tools
287+
* Create workflow manipulation tools with timeout protection
291288
*/
292289
private createWorkflowTools(): Record<string, any> {
293-
const addOperatorTool = createAddOperatorTool(
294-
this.workflowActionService,
295-
this.workflowUtilService,
296-
this.operatorMetadataService,
297-
this.copilotCoeditorService
290+
const addOperatorTool = toolWithTimeout(
291+
createAddOperatorTool(
292+
this.workflowActionService,
293+
this.workflowUtilService,
294+
this.operatorMetadataService,
295+
this.copilotCoeditorService
296+
)
298297
);
299-
const addLinkTool = createAddLinkTool(this.workflowActionService);
300-
const listOperatorsTool = createListOperatorsTool(this.workflowActionService, this.copilotCoeditorService);
301-
const listLinksTool = createListLinksTool(this.workflowActionService);
302-
const listOperatorTypesTool = createListOperatorTypesTool(this.workflowUtilService);
303-
const getOperatorTool = createGetOperatorTool(this.workflowActionService, this.copilotCoeditorService);
304-
const deleteOperatorTool = createDeleteOperatorTool(this.workflowActionService, this.copilotCoeditorService);
305-
const deleteLinkTool = createDeleteLinkTool(this.workflowActionService);
306-
const setOperatorPropertyTool = createSetOperatorPropertyTool(
307-
this.workflowActionService,
308-
this.copilotCoeditorService
298+
const addLinkTool = toolWithTimeout(createAddLinkTool(this.workflowActionService));
299+
const listOperatorsTool = toolWithTimeout(
300+
createListOperatorsTool(this.workflowActionService, this.copilotCoeditorService)
309301
);
310-
const getOperatorSchemaTool = createGetOperatorSchemaTool(
311-
this.workflowActionService,
312-
this.operatorMetadataService,
313-
this.copilotCoeditorService
302+
const listLinksTool = toolWithTimeout(createListLinksTool(this.workflowActionService));
303+
const listOperatorTypesTool = toolWithTimeout(createListOperatorTypesTool(this.workflowUtilService));
304+
const getOperatorTool = toolWithTimeout(
305+
createGetOperatorTool(this.workflowActionService, this.copilotCoeditorService)
314306
);
315-
const getOperatorInputSchemaTool = createGetOperatorInputSchemaTool(
316-
this.workflowCompilingService,
317-
this.copilotCoeditorService
307+
const deleteOperatorTool = toolWithTimeout(
308+
createDeleteOperatorTool(this.workflowActionService, this.copilotCoeditorService)
318309
);
319-
const getWorkflowCompilationStateTool = createGetWorkflowCompilationStateTool(this.workflowCompilingService);
320-
const executeWorkflowTool = createExecuteWorkflowTool(this.executeWorkflowService);
321-
const getExecutionStateTool = createGetExecutionStateTool(this.executeWorkflowService);
322-
const hasOperatorResultTool = createHasOperatorResultTool(
323-
this.workflowResultService,
324-
this.workflowActionService,
325-
this.copilotCoeditorService
310+
const deleteLinkTool = toolWithTimeout(createDeleteLinkTool(this.workflowActionService));
311+
const setOperatorPropertyTool = toolWithTimeout(
312+
createSetOperatorPropertyTool(this.workflowActionService, this.copilotCoeditorService)
326313
);
327-
const getOperatorResultSnapshotTool = createGetOperatorResultSnapshotTool(
328-
this.workflowResultService,
329-
this.workflowActionService,
330-
this.copilotCoeditorService
314+
const getOperatorSchemaTool = toolWithTimeout(
315+
createGetOperatorSchemaTool(this.workflowActionService, this.operatorMetadataService, this.copilotCoeditorService)
331316
);
332-
const getOperatorResultInfoTool = createGetOperatorResultInfoTool(
333-
this.workflowResultService,
334-
this.workflowActionService,
335-
this.copilotCoeditorService
317+
const getOperatorInputSchemaTool = toolWithTimeout(
318+
createGetOperatorInputSchemaTool(this.workflowCompilingService, this.copilotCoeditorService)
319+
);
320+
const getWorkflowCompilationStateTool = toolWithTimeout(
321+
createGetWorkflowCompilationStateTool(this.workflowCompilingService)
322+
);
323+
const executeWorkflowTool = toolWithTimeout(createExecuteWorkflowTool(this.executeWorkflowService));
324+
const getExecutionStateTool = toolWithTimeout(createGetExecutionStateTool(this.executeWorkflowService));
325+
const killWorkflowTool = toolWithTimeout(createKillWorkflowTool(this.executeWorkflowService));
326+
const hasOperatorResultTool = toolWithTimeout(
327+
createHasOperatorResultTool(this.workflowResultService, this.workflowActionService, this.copilotCoeditorService)
328+
);
329+
const getOperatorResultPageTool = toolWithTimeout(
330+
createGetOperatorResultPageTool(
331+
this.workflowResultService,
332+
this.workflowActionService,
333+
this.copilotCoeditorService
334+
)
335+
);
336+
const getOperatorResultInfoTool = toolWithTimeout(
337+
createGetOperatorResultInfoTool(
338+
this.workflowResultService,
339+
this.workflowActionService,
340+
this.copilotCoeditorService
341+
)
342+
);
343+
const getWorkflowValidationErrorsTool = toolWithTimeout(
344+
createGetWorkflowValidationErrorsTool(this.validationWorkflowService)
345+
);
346+
const validateOperatorTool = toolWithTimeout(
347+
createValidateOperatorTool(this.validationWorkflowService, this.copilotCoeditorService)
348+
);
349+
const getValidOperatorsTool = toolWithTimeout(
350+
createGetValidOperatorsTool(this.validationWorkflowService, this.workflowActionService)
336351
);
337352

338353
// Get MCP tools in AI SDK format
@@ -353,10 +368,13 @@ export class TexeraCopilot {
353368
getOperatorInputSchema: getOperatorInputSchemaTool,
354369
getWorkflowCompilationState: getWorkflowCompilationStateTool,
355370
executeWorkflow: executeWorkflowTool,
356-
getExecutionState: getExecutionStateTool,
357-
hasOperatorResult: hasOperatorResultTool,
358-
getOperatorResultSnapshot: getOperatorResultSnapshotTool,
371+
// killWorkflow: killWorkflowTool,
372+
// hasOperatorResult: hasOperatorResultTool,
373+
// getOperatorResultPage: getOperatorResultPageTool,
359374
getOperatorResultInfo: getOperatorResultInfoTool,
375+
getWorkflowValidationErrors: getWorkflowValidationErrorsTool,
376+
validateOperator: validateOperatorTool,
377+
getValidOperators: getValidOperatorsTool,
360378
};
361379
}
362380

0 commit comments

Comments
 (0)