Repository: web-infra-dev/midscene
File: packages/shared/src/env/model-config-manager.ts (TypeScript)

Code before:
  private modelTimeout?: number;

  constructor(
    modelConfig?: TModelConfig,
    createOpenAIClientFn?: CreateOpenAIClientFn,
    modelTimeout?: number,
  ) {
    this.modelConfig = modelConfig;
    this.createOpenAIClientFn = createOpenAIClientFn;
    this.modelTimeout = modelTimeout;
  }

  private initialize() {
    if (this.isInitialized) {
      return;
    }

    let configMap: Record<string, string | undefined>;
    if (this.modelConfig) {
      this.isolatedMode = true;
      configMap = this.normalizeModelConfig(this.modelConfig);
    } else {
      configMap = this.globalConfigManager?.getAllEnvConfig() || {};
    }

    // Resolve timeout priority (modelTimeout always takes precedence if provided):
    // - If modelConfig provided (isolated mode): modelTimeout > modelConfig.MIDSCENE_MODEL_TIMEOUT > undefined
    // - If no modelConfig: modelTimeout > MIDSCENE_MODEL_TIMEOUT env var > undefined
    const timeoutFromConfigMap = configMap[MIDSCENE_MODEL_TIMEOUT];
    const resolvedTimeout =
      this.modelTimeout ??
      (timeoutFromConfigMap ? Number(timeoutFromConfigMap) : undefined);

    const defaultConfig = decideModelConfigFromIntentConfig(
      'default',
      configMap,
    );
    if (!defaultConfig) {
      throw new Error(
        'default model config is not found, which should not happen',
      );
    }

    const insightConfig = decideModelConfigFromIntentConfig(
      'insight',
      configMap,
    );

    const planningConfig = decideModelConfigFromIntentConfig(
      'planning',
      configMap,

Review comment (suggestion):
⁠We should add two more variables such as `MIDSCENE_PLANNING_MODEL_TIMEOUT` and `MIDSCENE_INSIGHT_MODEL_TIMEOUT`, therefore there is no need for a special "resolvedTimeout".

Code after:
  constructor(
    modelConfig?: TModelConfig,
    createOpenAIClientFn?: CreateOpenAIClientFn,
  ) {
    this.modelConfig = modelConfig;
    this.createOpenAIClientFn = createOpenAIClientFn;
  }

  private initialize() {
    if (this.isInitialized) {
      return;
    }

    let configMap: Record<string, string | undefined>;
    if (this.modelConfig) {
      this.isolatedMode = true;
      configMap = this.normalizeModelConfig(this.modelConfig);
    } else {
      configMap = this.globalConfigManager?.getAllEnvConfig() || {};
    }

    const defaultConfig = decideModelConfigFromIntentConfig(
      'default',
      configMap,
    );
    if (!defaultConfig) {
      throw new Error(
        'default model config is not found, which should not happen',
      );
    }

    const insightConfig = decideModelConfigFromIntentConfig(
      'insight',
      configMap,
    );

    const planningConfig = decideModelConfigFromIntentConfig(
      'planning',
      configMap,
    );

    // Each intent uses its own timeout from parsed config (MIDSCENE_MODEL_TIMEOUT,
    // MIDSCENE_INSIGHT_MODEL_TIMEOUT, MIDSCENE_PLANNING_MODEL_TIMEOUT).
    this.modelConfigMap = {
      default: {
        ...defaultConfig,
        createOpenAIClient: this.createOpenAIClientFn,
      },
      insight: {
        ...(insightConfig || defaultConfig),
        createOpenAIClient: this.createOpenAIClientFn,