kya-validator 0.2.3

Rust core KYA (Know Your Agent) validator with Python bindings, TEE support, and blockchain integration
Documentation
/**
 * ParameterEditor - Component for editing prompt parameters.
 */
import { useState, useEffect } from 'react';
import { useDemoStore, PromptConfig, PromptParameter } from '../../store/demoStore';

export interface ParameterEditorProps {
  prompt: PromptConfig;
  onParametersChange: (parameters: Record<string, unknown>) => void;
}

export default function ParameterEditor({ prompt, onParametersChange }: ParameterEditorProps) {
  const { promptParameters } = useDemoStore();
  const [parameters, setParameters] = useState<Record<string, unknown>>(promptParameters);
  const [newParameter, setNewParameter] = useState<PromptParameter>({
    name: '',
    type: 'string',
    defaultValue: '',
    description: '',
    enumValues: [],
  });

  // Update local state when prompt changes
  useEffect(() => {
    setParameters(promptParameters);
  }, [promptParameters]);

  // Infer parameter type from value
  const inferType = (value: unknown): 'string' | 'number' | 'boolean' | 'enum' => {
    if (typeof value === 'boolean') return 'boolean';
    if (typeof value === 'number') return 'number';
    if (Array.isArray(value)) return 'enum';
    return 'string';
  };

  // Get parameter type badge color
  const getTypeBadgeColor = (type: string): string => {
    switch (type) {
      case 'string':
        return 'bg-blue-500/20 text-blue-400';
      case 'number':
        return 'bg-green-500/20 text-green-400';
      case 'boolean':
        return 'bg-purple-500/20 text-purple-400';
      case 'enum':
        return 'bg-orange-500/20 text-orange-400';
      default:
        return 'bg-slate-500/20 text-slate-400';
    }
  };

  // Handle parameter value change
  const handleParameterChange = (name: string, value: unknown) => {
    const newParams = { ...parameters, [name]: value };
    setParameters(newParams);
    onParametersChange(newParams);
  };

  // Add new parameter
  const handleAddParameter = () => {
    if (!newParameter.name.trim()) {
      return;
    }

    const value = convertValue(newParameter.defaultValue, newParameter.type);
    const newParams = { ...parameters, [newParameter.name]: value };
    setParameters(newParams);
    onParametersChange(newParams);

    // Reset form
    setNewParameter({
      name: '',
      type: 'string',
      defaultValue: '',
      description: '',
      enumValues: [],
    });
  };

  // Remove parameter
  const handleRemoveParameter = (name: string) => {
    const newParams = { ...parameters };
    delete newParams[name];
    setParameters(newParams);
    onParametersChange(newParams);
  };

  // Convert value based on type
  const convertValue = (value: unknown, type: string): unknown => {
    switch (type) {
      case 'number':
        return typeof value === 'number' ? value : parseFloat(String(value)) || 0;
      case 'boolean':
        return value === true || value === 'true' || value === '1';
      case 'enum':
        return Array.isArray(value) ? value : [String(value)];
      default:
        return String(value);
    }
  };

  // Render input based on type
  const renderParameterInput = (name: string, value: unknown, type: string) => {
    const inferredType = inferType(value);

    switch (inferredType) {
      case 'boolean':
        return (
          <select
            value={String(value)}
            onChange={(e) => handleParameterChange(name, e.target.value === 'true')}
            className="w-full bg-slate-600 border border-slate-500 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:ring-2 focus:ring-cyan-500"
          >
            <option value="true">true</option>
            <option value="false">false</option>
          </select>
        );

      case 'number':
        return (
          <input
            type="number"
            value={String(value)}
            onChange={(e) => handleParameterChange(name, parseFloat(e.target.value) || 0)}
            className="w-full bg-slate-600 border border-slate-500 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:ring-2 focus:ring-cyan-500"
          />
        );

      case 'enum':
        const enumValues = Array.isArray(value) ? value : [];
        return (
          <select
            value={String(enumValues[0] || '')}
            onChange={(e) => handleParameterChange(name, e.target.value)}
            className="w-full bg-slate-600 border border-slate-500 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:ring-2 focus:ring-cyan-500"
          >
            {enumValues.map((val) => (
              <option key={String(val)} value={String(val)}>
                {String(val)}
              </option>
            ))}
          </select>
        );

      default:
        return (
          <input
            type="text"
            value={String(value)}
            onChange={(e) => handleParameterChange(name, e.target.value)}
            className="w-full bg-slate-600 border border-slate-500 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:ring-2 focus:ring-cyan-500 font-mono"
          />
        );
    }
  };

  return (
    <div className="bg-slate-800/50 rounded-xl p-6 border border-slate-700">
      <div className="flex items-center justify-between mb-4">
        <h3 className="text-lg font-semibold text-white">Parameters</h3>
        <span className="text-sm text-slate-400">
          {Object.keys(parameters).length} parameters
        </span>
      </div>

      {/* Existing Parameters */}
      {Object.keys(parameters).length > 0 ? (
        <div className="space-y-3 mb-6">
          {Object.entries(parameters).map(([name, value]) => {
            const type = inferType(value);
            return (
              <div
                key={name}
                className="p-4 bg-slate-700/50 rounded-lg border border-slate-600"
              >
                <div className="flex items-start justify-between mb-2">
                  <div className="flex items-center gap-2">
                    <span className="font-mono text-sm text-cyan-400">{`{${name}}`}</span>
                    <span
                      className={`px-2 py-0.5 rounded text-xs font-medium ${getTypeBadgeColor(type)}`}
                    >
                      {type}
                    </span>
                  </div>
                  <button
                    onClick={() => handleRemoveParameter(name)}
                    className="p-1 text-slate-400 hover:text-red-400 transition-colors"
                    title="Remove parameter"
                  >
                    <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                      <path
                        strokeLinecap="round"
                        strokeLinejoin="round"
                        strokeWidth={2}
                        d="M6 18L18 6M6 6l12 12"
                      />
                    </svg>
                  </button>
                </div>
                <div className="flex items-center gap-3">
                  <div className="flex-1">
                    <label className="block text-xs text-slate-400 mb-1">
                      Value
                    </label>
                    {renderParameterInput(name, value, type)}
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      ) : (
        <div className="mb-6 p-8 text-center bg-slate-700/30 rounded-lg border border-dashed border-slate-600">
          <svg className="w-12 h-12 mx-auto text-slate-500 mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path
              strokeLinecap="round"
              strokeLinejoin="round"
              strokeWidth={2}
              d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"
            />
          </svg>
          <p className="text-sm text-slate-400">
            No parameters defined. Add parameters below.
          </p>
        </div>
      )}

      {/* Add New Parameter Form */}
      <div className="p-4 bg-slate-700/30 rounded-lg border border-slate-600">
        <h4 className="text-sm font-medium text-slate-300 mb-3">Add Parameter</h4>
        <div className="grid grid-cols-1 md:grid-cols-2 gap-3 mb-3">
          <div>
            <label className="block text-xs text-slate-400 mb-1">
              Parameter Name
            </label>
            <input
              type="text"
              value={newParameter.name}
              onChange={(e) =>
                setNewParameter({ ...newParameter, name: e.target.value })
              }
              placeholder="e.g., urgency_level"
              className="w-full bg-slate-600 border border-slate-500 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:ring-2 focus:ring-cyan-500 font-mono"
            />
          </div>
          <div>
            <label className="block text-xs text-slate-400 mb-1">Type</label>
            <select
              value={newParameter.type}
              onChange={(e) =>
                setNewParameter({
                  ...newParameter,
                  type: e.target.value as 'string' | 'number' | 'boolean' | 'enum',
                })
              }
              className="w-full bg-slate-600 border border-slate-500 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:ring-2 focus:ring-cyan-500"
            >
              <option value="string">String</option>
              <option value="number">Number</option>
              <option value="boolean">Boolean</option>
              <option value="enum">Enum</option>
            </select>
          </div>
        </div>
        <div className="mb-3">
          <label className="block text-xs text-slate-400 mb-1">Default Value</label>
          <input
            type="text"
            value={String(newParameter.defaultValue)}
            onChange={(e) =>
              setNewParameter({ ...newParameter, defaultValue: e.target.value })
            }
            placeholder="e.g., critical"
            className="w-full bg-slate-600 border border-slate-500 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:ring-2 focus:ring-cyan-500"
          />
        </div>
        <div className="mb-3">
          <label className="block text-xs text-slate-400 mb-1">Description (optional)</label>
          <input
            type="text"
            value={newParameter.description}
            onChange={(e) =>
              setNewParameter({ ...newParameter, description: e.target.value })
            }
            placeholder="Brief description of this parameter"
            className="w-full bg-slate-600 border border-slate-500 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:ring-2 focus:ring-cyan-500"
          />
        </div>
        <button
          onClick={handleAddParameter}
          disabled={!newParameter.name.trim()}
          className="w-full flex items-center justify-center gap-2 bg-cyan-500 hover:bg-cyan-600 disabled:opacity-50 disabled:cursor-not-allowed text-white font-medium py-2 px-4 rounded-lg transition-all duration-200"
        >
          <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path
              strokeLinecap="round"
              strokeLinejoin="round"
              strokeWidth={2}
              d="M12 4v16m8-8H4"
            />
          </svg>
          Add Parameter
        </button>
      </div>
    </div>
  );
}