Xiao-zhen-Liu commented on code in PR #4020:
URL: https://github.com/apache/texera/pull/4020#discussion_r2492288739


##########
frontend/src/app/workspace/service/workflow-graph/model/workflow-action.service.ts:
##########
@@ -919,4 +919,56 @@ export class WorkflowActionService {
   public getHighlightingEnabled() {
     return this.highlightingEnabled;
   }
+
+  /**
+   * Find all operators and links on any upstream path leading to the given 
destination operator.
+   * Uses BFS to traverse backwards from the destination to find all 
contributing operators.
+   * @param destinationOperatorId The operator ID to find upstream paths to
+   * @returns Object containing arrays of operator IDs and link IDs on 
upstream paths
+   */
+  public findUpstreamPath(destinationOperatorId: string): { operators: 
string[]; links: string[] } {

Review Comment:
   Is this needed for this PR?



##########
frontend/src/app/workspace/service/copilot/copilot-prompts.ts:
##########
@@ -0,0 +1,160 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * System prompts for Texera Copilot
+ */
+
+export const COPILOT_SYSTEM_PROMPT = `# Texera Copilot
+
+You are Texera Copilot, an AI assistant for building and modifying data 
workflows.
+
+## Task
+Your task is to find out the data error using workflow.

Review Comment:
   This is not the task we want in the main branch, right?



##########
bin/litellm-config.yaml:
##########
@@ -0,0 +1,9 @@
+model_list:
+  - model_name: claude-3.7
+    litellm_params:
+      model: claude-3-7-sonnet-20250219

Review Comment:
   This model is 
[deprecated](https://docs.claude.com/en/docs/about-claude/model-deprecations) 
since Oct. 28. I was not able to access this model when testing it.



##########
frontend/src/app/workspace/service/copilot/texera-copilot.ts:
##########
@@ -0,0 +1,481 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { Injectable } from "@angular/core";
+import { BehaviorSubject, Observable, from } from "rxjs";
+import { WorkflowActionService } from 
"../workflow-graph/model/workflow-action.service";
+import {
+  createAddOperatorTool,
+  createAddLinkTool,
+  createActionPlanTool,
+  createUpdateActionPlanProgressTool,
+  createGetActionPlanTool,
+  createListActionPlansTool,
+  createDeleteActionPlanTool,
+  createUpdateActionPlanTool,
+  createGetOperatorTool,
+  createDeleteOperatorTool,
+  createDeleteLinkTool,
+  createSetOperatorPropertyTool,
+  createSetPortPropertyTool,
+  createGetOperatorSchemaTool,
+  createGetOperatorPropertiesSchemaTool,
+  createGetOperatorPortsInfoTool,
+  createGetOperatorMetadataTool,
+  createGetOperatorInputSchemaTool,
+  createGetOperatorOutputSchemaTool,
+  createGetWorkflowCompilationStateTool,
+  createExecuteWorkflowTool,
+  createGetExecutionStateTool,
+  createKillWorkflowTool,
+  createHasOperatorResultTool,
+  createGetOperatorResultTool,
+  createGetOperatorResultInfoTool,
+  createGetValidationInfoOfCurrentWorkflowTool,
+  createValidateOperatorTool,
+  toolWithTimeout,
+  createListAllOperatorTypesTool,
+  createListLinksTool,
+  createListOperatorIdsTool,
+} from "./workflow-tools";
+import { OperatorMetadataService } from 
"../operator-metadata/operator-metadata.service";
+import { createOpenAI } from "@ai-sdk/openai";
+import { AssistantModelMessage, generateText, type ModelMessage, stepCountIs, 
UIMessage, UserModelMessage } from "ai";
+import { WorkflowUtilService } from 
"../workflow-graph/util/workflow-util.service";
+import { AppSettings } from "../../../common/app-setting";
+import { DynamicSchemaService } from 
"../dynamic-schema/dynamic-schema.service";
+import { ExecuteWorkflowService } from 
"../execute-workflow/execute-workflow.service";
+import { WorkflowResultService } from 
"../workflow-result/workflow-result.service";
+import { WorkflowCompilingService } from 
"../compile-workflow/workflow-compiling.service";
+import { ValidationWorkflowService } from 
"../validation/validation-workflow.service";
+import { COPILOT_SYSTEM_PROMPT, PLANNING_MODE_PROMPT } from 
"./copilot-prompts";
+import { ActionPlanService } from "../action-plan/action-plan.service";
+import { NotificationService } from 
"../../../common/service/notification/notification.service";
+
+export const DEFAULT_AGENT_MODEL_ID = "claude-3.7";
+
+/**
+ * Copilot state enum.
+ */
+export enum CopilotState {
+  UNAVAILABLE = "Unavailable",
+  AVAILABLE = "Available",
+  GENERATING = "Generating",
+  STOPPING = "Stopping",
+}
+
+/**
+ * Agent response for UI display.
+ */
+export interface AgentUIMessage {
+  role: "user" | "agent";
+  content: string;
+  isBegin: boolean;
+  isEnd: boolean;
+  toolCalls?: any[];
+  toolResults?: any[];
+  usage?: {
+    inputTokens?: number;
+    outputTokens?: number;
+    totalTokens?: number;
+    cachedInputTokens?: number;
+  };
+}
+
+/**
+ * Texera Copilot - An AI assistant for workflow manipulation.
+ * Uses Vercel AI SDK for chat completion.
+ * Note: Not a singleton - each agent has its own instance.
+ */
+@Injectable()
+export class TexeraCopilot {
+  private model: any;
+  private modelType: string;
+  private agentId: string = "";
+  private agentName: string = "";
+  private messages: ModelMessage[] = [];
+  private agentResponses: AgentUIMessage[] = [];
+  private agentResponsesSubject = new BehaviorSubject<AgentUIMessage[]>([]);
+  public agentResponses$ = this.agentResponsesSubject.asObservable();
+  private state: CopilotState = CopilotState.UNAVAILABLE;
+  private shouldStopAfterActionPlan: boolean = false;
+  private planningMode: boolean = false;
+
+  constructor(
+    private workflowActionService: WorkflowActionService,
+    private workflowUtilService: WorkflowUtilService,
+    private operatorMetadataService: OperatorMetadataService,
+    private dynamicSchemaService: DynamicSchemaService,
+    private executeWorkflowService: ExecuteWorkflowService,
+    private workflowResultService: WorkflowResultService,
+    private workflowCompilingService: WorkflowCompilingService,
+    private validationWorkflowService: ValidationWorkflowService,
+    private actionPlanService: ActionPlanService,
+    private notificationService: NotificationService
+  ) {
+    this.modelType = DEFAULT_AGENT_MODEL_ID;
+  }
+
+  public setAgentInfo(agentId: string, agentName: string): void {
+    this.agentId = agentId;
+    this.agentName = agentName;
+  }
+
+  public setModelType(modelType: string): void {
+    this.modelType = modelType;
+  }
+
+  public setPlanningMode(planningMode: boolean): void {
+    this.planningMode = planningMode;
+  }
+
+  public getPlanningMode(): boolean {
+    return this.planningMode;
+  }
+
+  /**
+   * Type guard to check if a message is a valid ModelMessage.
+   * Uses TypeScript's type predicate for compile-time type safety.
+   */
+  private isValidModelMessage(message: unknown): message is ModelMessage {
+    if (!message || typeof message !== "object") {
+      return false;
+    }
+
+    const msg = message as Record<string, unknown>;
+
+    // Check if role property exists and is a string
+    if (typeof msg.role !== "string") {
+      return false;
+    }
+
+    // Validate based on role using type narrowing
+    switch (msg.role) {
+      case "user":
+        // UserModelMessage: { role: "user", content: string }
+        return typeof msg.content === "string";
+
+      case "assistant":
+        // AssistantModelMessage: { role: "assistant", content: string | array 
}
+        return typeof msg.content === "string" || Array.isArray(msg.content);
+
+      case "tool":
+        // ToolModelMessage: { role: "tool", content: array }
+        return Array.isArray(msg.content);
+
+      default:
+        return false;
+    }
+  }
+
+  /**
+   * Validate all messages in the conversation history.
+   * Throws an error if any message doesn't conform to ModelMessage type.
+   */
+  private validateMessages(): void {
+    const invalidMessages: Array<{ index: number; message: unknown }> = [];
+
+    this.messages.forEach((message, index) => {
+      if (!this.isValidModelMessage(message)) {
+        invalidMessages.push({ index, message });
+      }
+    });
+
+    if (invalidMessages.length > 0) {
+      const indices = invalidMessages.map(m => m.index).join(", ");
+      const details = invalidMessages.map(m => `[${m.index}]: 
${JSON.stringify(m.message)}`).join("; ");
+      const errorMessage = `Invalid ModelMessage(s) found at indices: 
${indices}. Details: ${details}`;
+
+      this.notificationService.error(
+        `Message validation failed: ${invalidMessages.length} invalid 
message(s). Please disconnect current agent and create a new agent`
+      );
+      throw new Error(errorMessage);
+    }
+  }
+
+  /**
+   * Initialize the copilot with the AI model.
+   */
+  public async initialize(): Promise<void> {
+    try {
+      this.model = createOpenAI({
+        baseURL: new URL(`${AppSettings.getApiEndpoint()}`, 
document.baseURI).toString(),
+        apiKey: "dummy",
+      }).chat(this.modelType);
+
+      this.state = CopilotState.AVAILABLE;
+    } catch (error: unknown) {
+      this.state = CopilotState.UNAVAILABLE;
+      throw error;
+    }
+  }
+
+  public sendMessage(message: string): Observable<void> {

Review Comment:
   Can you try to refactor this method to reduce its complexity and make it 
more understandable?



##########
frontend/custom-webpack.config.js:
##########
@@ -18,6 +18,12 @@
  */
 
 module.exports = {
+  resolve: {
+    fallback: {
+      // Minimal polyfill for path (needed by some dependencies)

Review Comment:
   Which dependencies need this?



##########
bin/mcp-service.sh:
##########


Review Comment:
   Is this needed?



##########
bin/build-services.sh:
##########


Review Comment:
   Is this needed?



##########
build.sbt:
##########
@@ -114,4 +114,4 @@ lazy val TexeraProject = (project in file("."))
     organization := "org.apache",
     scalaVersion := "2.13.12",
     publishMavenStyle := true
-  )

Review Comment:
   This looks like an unnecessary change.



##########
frontend/src/app/workspace/service/copilot/texera-copilot.ts:
##########
@@ -0,0 +1,481 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { Injectable } from "@angular/core";
+import { BehaviorSubject, Observable, from } from "rxjs";
+import { WorkflowActionService } from 
"../workflow-graph/model/workflow-action.service";
+import {
+  createAddOperatorTool,
+  createAddLinkTool,
+  createActionPlanTool,
+  createUpdateActionPlanProgressTool,
+  createGetActionPlanTool,
+  createListActionPlansTool,
+  createDeleteActionPlanTool,
+  createUpdateActionPlanTool,
+  createGetOperatorTool,
+  createDeleteOperatorTool,
+  createDeleteLinkTool,
+  createSetOperatorPropertyTool,
+  createSetPortPropertyTool,
+  createGetOperatorSchemaTool,
+  createGetOperatorPropertiesSchemaTool,
+  createGetOperatorPortsInfoTool,
+  createGetOperatorMetadataTool,
+  createGetOperatorInputSchemaTool,
+  createGetOperatorOutputSchemaTool,
+  createGetWorkflowCompilationStateTool,
+  createExecuteWorkflowTool,
+  createGetExecutionStateTool,
+  createKillWorkflowTool,
+  createHasOperatorResultTool,
+  createGetOperatorResultTool,
+  createGetOperatorResultInfoTool,
+  createGetValidationInfoOfCurrentWorkflowTool,
+  createValidateOperatorTool,
+  toolWithTimeout,
+  createListAllOperatorTypesTool,
+  createListLinksTool,
+  createListOperatorIdsTool,
+} from "./workflow-tools";
+import { OperatorMetadataService } from 
"../operator-metadata/operator-metadata.service";
+import { createOpenAI } from "@ai-sdk/openai";
+import { AssistantModelMessage, generateText, type ModelMessage, stepCountIs, 
UIMessage, UserModelMessage } from "ai";
+import { WorkflowUtilService } from 
"../workflow-graph/util/workflow-util.service";
+import { AppSettings } from "../../../common/app-setting";
+import { DynamicSchemaService } from 
"../dynamic-schema/dynamic-schema.service";
+import { ExecuteWorkflowService } from 
"../execute-workflow/execute-workflow.service";
+import { WorkflowResultService } from 
"../workflow-result/workflow-result.service";
+import { WorkflowCompilingService } from 
"../compile-workflow/workflow-compiling.service";
+import { ValidationWorkflowService } from 
"../validation/validation-workflow.service";
+import { COPILOT_SYSTEM_PROMPT, PLANNING_MODE_PROMPT } from 
"./copilot-prompts";
+import { ActionPlanService } from "../action-plan/action-plan.service";
+import { NotificationService } from 
"../../../common/service/notification/notification.service";
+
+export const DEFAULT_AGENT_MODEL_ID = "claude-3.7";
+
+/**
+ * Copilot state enum.
+ */
+export enum CopilotState {
+  UNAVAILABLE = "Unavailable",
+  AVAILABLE = "Available",
+  GENERATING = "Generating",
+  STOPPING = "Stopping",
+}
+
+/**
+ * Agent response for UI display.
+ */
+export interface AgentUIMessage {
+  role: "user" | "agent";
+  content: string;
+  isBegin: boolean;
+  isEnd: boolean;
+  toolCalls?: any[];
+  toolResults?: any[];
+  usage?: {
+    inputTokens?: number;
+    outputTokens?: number;
+    totalTokens?: number;
+    cachedInputTokens?: number;
+  };
+}
+
+/**
+ * Texera Copilot - An AI assistant for workflow manipulation.
+ * Uses Vercel AI SDK for chat completion.
+ * Note: Not a singleton - each agent has its own instance.
+ */
+@Injectable()
+export class TexeraCopilot {
+  private model: any;
+  private modelType: string;
+  private agentId: string = "";
+  private agentName: string = "";
+  private messages: ModelMessage[] = [];
+  private agentResponses: AgentUIMessage[] = [];
+  private agentResponsesSubject = new BehaviorSubject<AgentUIMessage[]>([]);
+  public agentResponses$ = this.agentResponsesSubject.asObservable();
+  private state: CopilotState = CopilotState.UNAVAILABLE;
+  private shouldStopAfterActionPlan: boolean = false;
+  private planningMode: boolean = false;
+
+  constructor(
+    private workflowActionService: WorkflowActionService,
+    private workflowUtilService: WorkflowUtilService,
+    private operatorMetadataService: OperatorMetadataService,
+    private dynamicSchemaService: DynamicSchemaService,

Review Comment:
   This is unused.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to