ronantakizawa/codereview-bench huggingface dataset:
Repository: Genesis-Embodied-AI/Genesis
File: genesis/constants.py (Python)

Code before:
    CAPSULE = 4
    BOX = 5
    MESH = 6
    TERRAIN = 7


# joint type in rigid solver, ranked by number of dofs
class JOINT_TYPE(IntEnum):
    FIXED = 0
    REVOLUTE = 1
    PRISMATIC = 2
    SPHERICAL = 3
    FREE = 4


class EQUALITY_TYPE(IntEnum):
    CONNECT = 0
    WELD = 1
    JOINT = 2


class CTRL_MODE(IntEnum):
    FORCE = 0
    VELOCITY = 1
    POSITION = 2
    POSITION_VELOCITY = 3


######### User accessible constants do not capitalize #########
# rigid solver intergrator
class integrator(IntEnum):
    Euler = 0
    implicitfast = 1
    approximate_implicitfast = 2


# rigid solver constraint solver
class constraint_solver(IntEnum):
    CG = 0
    Newton = 1


# backend
class backend(IntEnum):
    cpu = 0
    gpu = 1
    cuda = 2
    vulkan = 3
    metal = 4
    opengl = 5


Review comment (suggestion):
I don't think internally you need this extra mode. I would just remove `POSITION` and use `POSITION_VELOCITY` everywhere instead.

Code after:
    CAPSULE = 4
    BOX = 5
    MESH = 6
    TERRAIN = 7


# joint type in rigid solver, ranked by number of dofs
class JOINT_TYPE(IntEnum):
    FIXED = 0
    REVOLUTE = 1
    PRISMATIC = 2
    SPHERICAL = 3
    FREE = 4


class EQUALITY_TYPE(IntEnum):
    CONNECT = 0
    WELD = 1
    JOINT = 2


class CTRL_MODE(IntEnum):
    FORCE = 0
    VELOCITY = 1
    POSITION = 2


######### User accessible constants do not capitalize #########
# rigid solver intergrator
class integrator(IntEnum):
    Euler = 0
    implicitfast = 1
    approximate_implicitfast = 2


# rigid solver constraint solver
class constraint_solver(IntEnum):
    CG = 0
    Newton = 1


# backend
class backend(IntEnum):
    cpu = 0
    gpu = 1
    cuda = 2
    vulkan = 3
    metal = 4
    opengl = 5

    def __format__(self, format_spec):
Repository: Genesis-Embodied-AI/Genesis
File: genesis/ext/pyrender/scene.py (Python)

Code before:

        # add every node with geometry to the pyrender scene
        for node in trimesh_scene.graph.nodes_geometry:
            pose, geom_name = trimesh_scene.graph[node]
            scene_pr.add(geometries[geom_name], pose=pose)

        return scene_pr

    def sorted_mesh_nodes(self):
        cam_pos = self.get_pose(self.main_camera_node)
        if len(cam_pos.shape) == 3:
            batched_pos = True
            cam_loc = cam_pos[:, :3, 3]
        else:
            batched_pos = False
            cam_loc = cam_pos[:3, 3]
        solid_nodes = []
        trans_nodes = []
        for node in self.mesh_nodes:
            mesh = node.mesh
            if mesh.is_transparent:
                trans_nodes.append(node)
            else:
                solid_nodes.append(node)

        # TODO BETTER SORTING METHOD
        if batched_pos:
            trans_nodes.sort(key=lambda n: -np.linalg.norm(self.get_pose(n)[:3, 3] - cam_loc[0]))
            solid_nodes.sort(key=lambda n: -np.linalg.norm(self.get_pose(n)[:3, 3] - cam_loc[0]))
        else:
            trans_nodes.sort(key=lambda n: -np.linalg.norm(self.get_pose(n)[:3, 3] - cam_loc))
            solid_nodes.sort(key=lambda n: -np.linalg.norm(self.get_pose(n)[:3, 3] - cam_loc))

        return solid_nodes + trans_nodes


Review comment (bug):
This part is happening before the rendering pass so we don't really have `env_idx` here.

Before (with `cam_loc` unbatched) we were already using this behavior. So this is not modifying behavior even if it looks wrong/odd.

Given the TODO I decided to leave it as-is as it might warrant a bigger change to "fix" this (if it's needed at all, I'm not sure how strict the ordering should be).

Code after:

        # add every node with geometry to the pyrender scene
        for node in trimesh_scene.graph.nodes_geometry:
            pose, geom_name = trimesh_scene.graph[node]
            scene_pr.add(geometries[geom_name], pose=pose)

        return scene_pr

    def sorted_mesh_nodes(self):
        cam_pos = self.get_pose(self.main_camera_node)
        cam_loc = cam_pos[..., :3, 3]
        batched_pos = len(cam_pos.shape) == 3
        solid_nodes = []
        trans_nodes = []
        for node in self.mesh_nodes:
            mesh = node.mesh
            if mesh.is_transparent:
                trans_nodes.append(node)
            else:
                solid_nodes.append(node)

        # TODO BETTER SORTING METHOD
        if batched_pos:
            # FIXME normally sorting should be done PER scene when having a batched rasterizer render
            trans_nodes.sort(key=lambda n: -np.linalg.norm(self.get_pose(n)[:3, 3] - cam_loc[0]))
            solid_nodes.sort(key=lambda n: -np.linalg.norm(self.get_pose(n)[:3, 3] - cam_loc[0]))
        else:
            trans_nodes.sort(key=lambda n: -np.linalg.norm(self.get_pose(n)[:3, 3] - cam_loc))
            solid_nodes.sort(key=lambda n: -np.linalg.norm(self.get_pose(n)[:3, 3] - cam_loc))

        return solid_nodes + trans_nodes
Repository: Genesis-Embodied-AI/Genesis
File: genesis/options/sensors/options.py (Python)

Code before:
from typing import Sequence

import numpy as np
from pydantic import Field

import genesis as gs
from genesis.engine.scene import Scene

from ..options import Options
from .raycaster import DepthCameraPattern, RaycastPattern

Tuple3FType = tuple[float, float, float]
MaybeTuple3FType = float | Tuple3FType
Matrix3x3Type = tuple[tuple[float, float, float], tuple[float, float, float], tuple[float, float, float]]
MaybeMatrix3x3Type = Matrix3x3Type | MaybeTuple3FType


class SensorOptions(Options):
    """
    Base class for all sensor options.

    Each sensor should have their own options class that inherits from this class.
    The options class should be registered with the SensorManager using the @register_sensor decorator.

    Parameters
    ----------
    delay : float
        The read delay time in seconds. Data read will be outdated by this amount. Defaults to 0.0 (no delay).
    update_ground_truth_only : bool
        If True, the sensor will only update the ground truth data, and not the measured data. Defaults to False.
    draw_debug : bool
        If True and visualizer is active, the sensor will draw debug shapes in the scene. Defaults to False.

Review comment (bug):
This is a bug. You cannot do this. Guard this with `TYPE_CHECKING`

Code after:
from typing import TYPE_CHECKING, Sequence

import numpy as np
from pydantic import Field

import genesis as gs

from ..options import Options
from .raycaster import DepthCameraPattern, RaycastPattern

Tuple3FType = tuple[float, float, float]
MaybeTuple3FType = float | Tuple3FType
Matrix3x3Type = tuple[tuple[float, float, float], tuple[float, float, float], tuple[float, float, float]]
MaybeMatrix3x3Type = Matrix3x3Type | MaybeTuple3FType

if TYPE_CHECKING:
    from genesis.engine.scene import Scene


class SensorOptions(Options):
    """
    Base class for all sensor options.

    Each sensor should have their own options class that inherits from this class.
    The options class should be registered with the SensorManager using the @register_sensor decorator.

    Parameters
    ----------
    delay : float
        The read delay time in seconds. Data read will be outdated by this amount. Defaults to 0.0 (no delay).
    update_ground_truth_only : bool
        If True, the sensor will only update the ground truth data, and not the measured data. Defaults to False.
Repository: Genesis-Embodied-AI/Genesis
File: genesis/engine/mesh.py (Python)

Code before:
        Whether the mesh is convex.
        """
        return self._mesh.is_convex

    @property
    def metadata(self):
        """
        Metadata of the mesh.
        """
        return self._metadata

    @property
    def verts(self):
        """
        Vertices of the mesh.
        """
        return self._mesh.vertices

    @verts.setter
    def verts(self, verts):
        """
        Set the vertices of the mesh.
        """
        assert len(verts) == len(self.verts)
        self._mesh.vertices = verts

    @property
    def faces(self):
        """
        Faces of the mesh.
        """
        return self._mesh.faces

    @property
    def normals(self):
        """
        Normals of the mesh.
        """
        return self._mesh.vertex_normals

    @property
    def surface(self):
        """
        Surface of the mesh.
        """
        return self._surface

    @property
    def uvs(self):
        """
        UVs of the mesh.

Review comment (suggestion):
After removing the verts setter, https://github.com/Genesis-Embodied-AI/Genesis/blob/cd84ab234de030ab48f2f30f579f59cc158438d2/genesis/vis/rasterizer_context.py#L562 this line seems to throw the error.

Code after:
        """
        return self._mesh.is_convex

    @property
    def metadata(self):
        """
        Metadata of the mesh.
        """
        return self._metadata

    @property
    def verts(self):
        """
        Vertices of the mesh.
        """
        if self._scale is not None:
            return self._mesh.vertices * self._scale
        return self._mesh.vertices

    @property
    def faces(self):
        """
        Faces of the mesh.
        """
        return self._mesh.faces

    @property
    def normals(self):
        """
        Normals of the mesh.
        """
        if self._scale is not None and self._scale.size == 3:
            return self._mesh.vertex_normals * self._scale
        return self._mesh.vertex_normals

    @property
    def surface(self):
        """
        Surface of the mesh.
        """
        return self._surface

    @property
    def uvs(self):
        """
        UVs of the mesh.
        """
        return self._uvs

    @property
    def area(self):
Repository: GoogleCloudPlatform/kubectl-ai
File: agent.go (Go)

Code before:
		if err != nil {
			return fmt.Sprintf("Error executing bash command: %v", err), err
		}
		return output, nil
	default:
		a.addMessage(ctx, "system", fmt.Sprintf("Error: Tool %s not found", action.Name))
		logger.Info("Unknown action: ", "action", action.Name)
		return "", a.recordError(ctx, fmt.Errorf("unknown action: %s", action.Name))
	}
}

// AskLLM asks the LLM for the next action, sending a prompt including the .History
func (a *Agent) AskLLM(ctx context.Context) (*ReActResponse, error) {
	logger := loggerFromContext(ctx)
	logger.Info("Asking LLM...")

	data := Data{
		Query:       a.Query,
		PastQueries: a.PastQueries,
		History:     a.History(),
		Tools:       "kubectl, gcrane, bash",
	}

	prompt, err := a.generateFromTemplate(data)
	if err != nil {
		fmt.Println("Error generating from template:", err)
		return nil, err
	}

	logger.Info("Asking LLM", "prompt", prompt)
	a.record(ctx, &journal.Event{
		Timestamp: time.Now(),
		Action:    "llm-request",
		Payload:   prompt,
	})

	logger.Info("Thinking...", "prompt", prompt)

	response, err := a.ContentGenerator.GenerateCompletion(ctx, &gollm.CompletionRequest{
		Prompt: prompt,
	})
	if err != nil {
		return nil, fmt.Errorf("generating LLM completion: %w", err)
	}

	a.record(ctx, &journal.Event{
		Timestamp: time.Now(),
		Action:    "llm-response",
		Payload:   response,
	})


Review comment (suggestion):
I have run into situations where I got error rate-limiting error from llm provider, so wondering if we should record that here or in the caller above to help in resuming (if we ever get there :)).

Code after:
// AskLLM asks the LLM for the next action, sending a prompt including the .History
func (a *Agent) AskLLM(ctx context.Context) (*ReActResponse, error) {
	log := klog.FromContext(ctx)
	log.Info("Asking LLM...")

	data := Data{
		Query:       a.Query,
		PastQueries: a.PastQueries,
		History:     a.History(),
		Tools:       "kubectl, gcrane, bash",
	}

	prompt, err := a.generateFromTemplate(data)
	if err != nil {
		fmt.Println("Error generating from template:", err)
		return nil, err
	}

	log.Info("Thinking...", "prompt", prompt)

	response, err := a.ContentGenerator.GenerateCompletion(ctx, &gollm.CompletionRequest{
		Prompt: prompt,
	})
	if err != nil {
		return nil, fmt.Errorf("generating LLM completion: %w", err)
	}

	a.record(ctx, &journal.Event{
		Timestamp: time.Now(),
		Action:    "llm-response",
		Payload:   response,
	})

	reActResp, err := parseReActResponse(response.Response())
	if err != nil {
		return nil, fmt.Errorf("parsing ReAct response: %w", err)
	}
	return reActResp, nil
}

func sanitizeToolInput(input string) string {
	return strings.TrimSpace(input)
}

func (a *Agent) addMessage(ctx context.Context, role, content string) error {
	log := klog.FromContext(ctx)
	log.Info("Tracing...")

	msg := Message{
		Role:    role,
		Content: content,
Repository: GoogleCloudPlatform/kubectl-ai
File: agent.go (Go)

Code before:
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"text/template"
	"time"

	"github.com/GoogleCloudPlatform/kubectl-ai/gollm"
	"github.com/GoogleCloudPlatform/kubectl-ai/pkg/journal"
	"github.com/GoogleCloudPlatform/kubectl-ai/pkg/llmstrategy"
	"github.com/GoogleCloudPlatform/kubectl-ai/pkg/ui"
	"k8s.io/klog/v2"
)

// Agent knows how to execute a multi-step task. Goal is provided in the query argument.
type Agent struct {
	Model string

	Recorder journal.Recorder

	Strategy llmstrategy.Strategy
}

type Strategy struct {
	// TemplateFile allows specifying a custom template file
	TemplateFile string

	// Recorder captures events for diagnostics
	Recorder journal.Recorder

	RemoveWorkDir bool

	ContentGenerator gollm.Client

	Messages         []Message
	MaxIterations    int
	CurrentIteration int

	Query       string
	PastQueries string

	Kubeconfig string

	Tools map[string]func(input string, kubeconfig string, workDir string) (string, error)
}


Review comment (suggestion):
comment on the naming. I see these more of `AgentStrategy` or `AgentType` instead of `llm`.

Code after:
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"text/template"
	"time"

	"github.com/GoogleCloudPlatform/kubectl-ai/gollm"
	"github.com/GoogleCloudPlatform/kubectl-ai/pkg/journal"
	"github.com/GoogleCloudPlatform/kubectl-ai/pkg/llmstrategy"
	"github.com/GoogleCloudPlatform/kubectl-ai/pkg/ui"
	"k8s.io/klog/v2"
)

// Agent knows how to execute a multi-step task. Goal is provided in the query argument.
type Agent struct {
	Model string

	Recorder journal.Recorder

	Strategy llmstrategy.Strategy
}

type Strategy struct {
	AgentType AgentType

	LLM gollm.Client

	// PromptTemplateFile allows specifying a custom template file
	PromptTemplateFile string

	// Recorder captures events for diagnostics
	Recorder journal.Recorder

	RemoveWorkDir bool

	Messages         []Message
	MaxIterations    int
	CurrentIteration int

	Query       string
	PastQueries string

	Kubeconfig string

	Tools map[string]func(input string, kubeconfig string, workDir string) (string, error)
Repository: GoogleCloudPlatform/kubectl-ai
File: agent.go (Go)

Code before:
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"text/template"
	"time"

	"github.com/GoogleCloudPlatform/kubectl-ai/gollm"
	"github.com/GoogleCloudPlatform/kubectl-ai/pkg/journal"
	"github.com/GoogleCloudPlatform/kubectl-ai/pkg/llmstrategy"
	"github.com/GoogleCloudPlatform/kubectl-ai/pkg/ui"
	"k8s.io/klog/v2"
)

// Agent knows how to execute a multi-step task. Goal is provided in the query argument.
type Agent struct {
	Model string

	Recorder journal.Recorder

	Strategy llmstrategy.Strategy
}

type Strategy struct {
	// TemplateFile allows specifying a custom template file
	TemplateFile string

	// Recorder captures events for diagnostics
	Recorder journal.Recorder

	RemoveWorkDir bool

	ContentGenerator gollm.Client

	Messages         []Message
	MaxIterations    int
	CurrentIteration int

	Query       string
	PastQueries string


Review comment (suggestion):
another option to organize will be have different agent type structs, that all use common pieces to implement an agent type and top level `SuperAgent` is composed of sub-agents and in this case, only subagent type (ReAgent or ChatBasedAgent).

Code after:
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"text/template"
	"time"

	"github.com/GoogleCloudPlatform/kubectl-ai/gollm"
	"github.com/GoogleCloudPlatform/kubectl-ai/pkg/journal"
	"github.com/GoogleCloudPlatform/kubectl-ai/pkg/llmstrategy"
	"github.com/GoogleCloudPlatform/kubectl-ai/pkg/ui"
	"k8s.io/klog/v2"
)

// Agent knows how to execute a multi-step task. Goal is provided in the query argument.
type Agent struct {
	Model string

	Recorder journal.Recorder

	Strategy llmstrategy.Strategy
}

type Strategy struct {
	AgentType AgentType

	LLM gollm.Client

	// PromptTemplateFile allows specifying a custom template file
	PromptTemplateFile string

	// Recorder captures events for diagnostics
	Recorder journal.Recorder

	RemoveWorkDir bool

	Messages         []Message
	MaxIterations    int
	CurrentIteration int

	Query       string
Repository: GoogleCloudPlatform/kubectl-ai
File: main.go (Go)

Code before:
		if err != nil {
			return fmt.Errorf("failed to get default kubeconfig path: %w", err)
		}
		kubeconfigPath = p
	}

	if opt.MCPServer {
		workDir := "/tmp/kubectl-ai-mcp"
		if err := os.MkdirAll(workDir, 0755); err != nil {
			return fmt.Errorf("error creating work directory: %w", err)
		}
		mcpServer, err := newKubectlMCPServer(ctx, kubeconfigPath, tools.Default(), workDir)
		if err != nil {
			return fmt.Errorf("creating mcp server: %w", err)
		}
		return mcpServer.Serve(ctx)
	}

	// Check if stdin has data (is not a terminal)
	stdinInfo, _ := os.Stdin.Stat()
	stdinHasData := (stdinInfo.Mode() & os.ModeCharDevice) == 0
	var queryFromCmd string

	switch {
	// Handle positional arguments and stdin
	case len(args) > 1:
		return fmt.Errorf("only one positional argument allowed")
	case stdinHasData:
		var b strings.Builder
		// If we have a positional argument, use it as a prefix
		if len(args) == 1 {
			b.WriteString(args[0])
			b.WriteString("\n")
		}
		// Read the rest from stdin
		scanner := bufio.NewScanner(os.Stdin)
		for scanner.Scan() {
			b.WriteString(scanner.Text())
			b.WriteString("\n")
		}
		if err := scanner.Err(); err != nil {
			return fmt.Errorf("reading stdin: %w", err)
		}
		queryFromCmd = strings.TrimSpace(b.String())
		if queryFromCmd == "" {
			return fmt.Errorf("no query provided from stdin")
		}
		// Use the positional argument as a query
	case len(args) == 1:
		queryFromCmd = args[0]
	}

Review comment (refactor):
because of cobra introduction, some logic SHOULD be refactored.

Positional args https://github.com/spf13/cobra/blob/main/site/content/user_guide.md#positional-and-custom-arguments should be treated that way instead.



Code after:

	// do this early, before the third-party code logs anything.
	redirectStdLogToKlog()

	// resolve kubeconfig path with priority: flag/env > KUBECONFIG > default path
	if err := resolveKubeConfigPath(&opt); err != nil {
		return fmt.Errorf("failed to resolve kubeconfig path: %w", err)
	}

	if opt.MCPServer {
		if err := startMCPServer(ctx, opt); err != nil {
			return fmt.Errorf("failed to start MCP server: %w", err)
		}
	}

	// After reading stdin, it is consumed
	hasStdInData, err := hasStdInData()
	if err != nil {
		return fmt.Errorf("failed to check if stdin has data: %w", err)
	}

	// Handles positional args or stdin
	queryFromCmd, err := resolveQueryInput(hasStdInData, args)
	if err != nil {
		return fmt.Errorf("failed to resolve query input %w", err)
	}

	klog.Info("Application started", "pid", os.Getpid())

	llmClient, err := gollm.NewClient(ctx, opt.ProviderID)
	if err != nil {
		return fmt.Errorf("creating llm client: %w", err)
	}
	defer llmClient.Close()

	var recorder journal.Recorder
	if opt.TracePath != "" {
		fileRecorder, err := journal.NewFileRecorder(opt.TracePath)
		if err != nil {
			return fmt.Errorf("creating trace recorder: %w", err)
		}
		defer fileRecorder.Close()
		recorder = fileRecorder
	} else {
		// Ensure we always have a recorder, to avoid nil checks
		recorder = &journal.LogRecorder{}
		defer recorder.Close()
	}

	doc := ui.NewDocument()
Repository: GoogleCloudPlatform/kubectl-ai
File: main.go (Go)

Code before:
		}
	}

	return nil
}

func main() {
	ctx := context.Background()

	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)

	// klog setup must happen before Cobra parses any flags
	// add commandline flags for logging
	klog.InitFlags(nil)

	// Config for logs:
	// — file-only:
	// flag.Set("logtostderr", "false")
	// flag.Set("log-file", "/tmp/kubectl-ai.log")
	//
	// — or stderr-only: Which is default of klog.
	// flag.Set("logtostderr", "true")

	flag.Set("logtostderr", "false")
	flag.Set("log_file", "/tmp/kubectl-ai.log")

	// cobra has to know that we pass pass flags with flag lib, otherwise it creates conflict with flags.parse() method
	rootCmd.PersistentFlags().AddGoFlagSet(flag.CommandLine)
	defer klog.Flush()

	go func() {
		sig := <-sigCh
		fmt.Fprintf(os.Stderr, "Received signal, shutting down... %s\n", sig)
		klog.Flush()
		os.Exit(0)
	}()

	var opt Options
	opt.InitDefaults()

	if err := bindCLIFlagsToViper(opt); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	setupViperEnv()

	if err := rootCmd.ExecuteContext(ctx); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)

Review comment (suggestion):
pl. replace "/tmp/kubectl-ai.log" with `filepath.Join(os.TempDir(), "kubectl-ai.log")` because "/tmp" is not available on windows. 

Just FYI, Also on unix systems, if TMPDIR is set, os.TempDir() will return that instead of "/tmp"

Code after:
		}
	}

	return nil
}

func main() {
	ctx := context.Background()

	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)

	// klog setup must happen before Cobra parses any flags
	// add commandline flags for logging
	klog.InitFlags(nil)

	flag.Set("logtostderr", "false")
	flag.Set("log_file", filepath.Join(os.TempDir(), "kubectl-ai.log"))

	// cobra has to know that we pass pass flags with flag lib, otherwise it creates conflict with flags.parse() method
	rootCmd.PersistentFlags().AddGoFlagSet(flag.CommandLine)
	defer klog.Flush()

	go func() {
		sig := <-sigCh
		fmt.Fprintf(os.Stderr, "Received signal, shutting down... %s\n", sig)
		klog.Flush()
		os.Exit(0)
	}()

	var opt Options
	opt.InitDefaults()

	if err := bindCLIFlagsToViper(opt); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	setupViperEnv()

	if err := rootCmd.ExecuteContext(ctx); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func bindCLIFlagsToViper(opt Options) error {
	f := rootCmd.Flags()
	f.IntVar(&opt.MaxIterations, "max-iterations", opt.MaxIterations, "maximum number of iterations agent will try before giving up")
	f.StringVar(&opt.KubeConfigPath, "kubeconfig", opt.KubeConfigPath, "path to kubeconfig file")
	f.StringVar(&opt.PromptTemplateFilePath, "prompt-template-file-path", opt.PromptTemplateFilePath, "path to custom prompt template file")