|
|
@@ -550,6 +550,184 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
|
|
|
placeholder={`Default: ${azureOpenAiDefaultApiVersion}`}
|
|
|
/>
|
|
|
)}
|
|
|
+
|
|
|
+ {/* Model Info Configuration */}
|
|
|
+ <div style={{ marginTop: 15, padding: 10, border: '1px solid var(--vscode-input-border)', borderRadius: 4 }}>
|
|
|
+ <div style={{ marginBottom: 10 }}>
|
|
|
+ <span style={{ fontWeight: 500, fontSize: '14px' }}>Model Configuration</span>
|
|
|
+ <p style={{ fontSize: '12px', color: 'var(--vscode-descriptionForeground)', margin: '5px 0' }}>
|
|
|
+ Configure the capabilities and pricing for your custom OpenAI-compatible model
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* Capabilities Section */}
|
|
|
+ <div style={{ marginBottom: 15 }}>
|
|
|
+ <span style={{ fontWeight: 500, fontSize: '12px', color: 'var(--vscode-descriptionForeground)' }}>Capabilities</span>
|
|
|
+ <div style={{ display: "flex", flexDirection: "column", gap: 8, marginTop: 5 }}>
|
|
|
+ <VSCodeTextField
|
|
|
+ value={apiConfiguration?.openAiCusModelInfo?.maxTokens?.toString() || openAiModelInfoSaneDefaults.maxTokens?.toString() || ""}
|
|
|
+ type="text"
|
|
|
+ style={{ width: "100%" }}
|
|
|
+ title="Maximum number of tokens the model can generate in a single response"
|
|
|
+ onInput={(e: any) => {
|
|
|
+ const value = parseInt(e.target.value)
|
|
|
+ setApiConfiguration({
|
|
|
+ ...apiConfiguration,
|
|
|
+ openAiCusModelInfo: {
|
|
|
+ ...(apiConfiguration?.openAiCusModelInfo || openAiModelInfoSaneDefaults),
|
|
|
+ maxTokens: isNaN(value) ? undefined : value
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }}
|
|
|
+ placeholder="e.g. 4096">
|
|
|
+ <span style={{ fontWeight: 500 }}>Max Output Tokens</span>
|
|
|
+ </VSCodeTextField>
|
|
|
+
|
|
|
+ <VSCodeTextField
|
|
|
+ value={apiConfiguration?.openAiCusModelInfo?.contextWindow?.toString() || openAiModelInfoSaneDefaults.contextWindow?.toString() || ""}
|
|
|
+ type="text"
|
|
|
+ style={{ width: "100%" }}
|
|
|
+ title="Total number of tokens (input + output) the model can process in a single request"
|
|
|
+ onInput={(e: any) => {
|
|
|
+ const parsed = parseInt(e.target.value)
|
|
|
+ setApiConfiguration({
|
|
|
+ ...apiConfiguration,
|
|
|
+ openAiCusModelInfo: {
|
|
|
+ ...(apiConfiguration?.openAiCusModelInfo || openAiModelInfoSaneDefaults),
|
|
|
+ contextWindow: e.target.value === "" ? undefined : (isNaN(parsed) ? openAiModelInfoSaneDefaults.contextWindow : parsed)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }}
|
|
|
+ placeholder="e.g. 128000">
|
|
|
+ <span style={{ fontWeight: 500 }}>Context Window Size</span>
|
|
|
+ </VSCodeTextField>
|
|
|
+
|
|
|
+ <div style={{ display: "flex", gap: 20, marginTop: 5 }}>
|
|
|
+ <VSCodeCheckbox
|
|
|
+ checked={apiConfiguration?.openAiCusModelInfo?.supportsImages ?? openAiModelInfoSaneDefaults.supportsImages}
|
|
|
+ title="Enable if the model can process and understand images in the input"
|
|
|
+ onChange={(e: any) => {
|
|
|
+ setApiConfiguration({
|
|
|
+ ...apiConfiguration,
|
|
|
+ openAiCusModelInfo: {
|
|
|
+ ...(apiConfiguration?.openAiCusModelInfo || openAiModelInfoSaneDefaults),
|
|
|
+ supportsImages: e.target.checked
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }}>
|
|
|
+ Supports Images
|
|
|
+ </VSCodeCheckbox>
|
|
|
+
|
|
|
+ <VSCodeCheckbox
|
|
|
+ checked={apiConfiguration?.openAiCusModelInfo?.supportsComputerUse ?? false}
|
|
|
+ title="Enable if the model can interact with the computer (execute commands, modify files, etc.)"
|
|
|
+ onChange={(e: any) => {
|
|
|
+ setApiConfiguration({
|
|
|
+ ...apiConfiguration,
|
|
|
+ openAiCusModelInfo: {
|
|
|
+ ...(apiConfiguration?.openAiCusModelInfo || openAiModelInfoSaneDefaults),
|
|
|
+ supportsComputerUse: e.target.checked
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }}>
|
|
|
+ Supports Computer Use
|
|
|
+ </VSCodeCheckbox>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* Pricing Section */}
|
|
|
+ <div>
|
|
|
+ <span style={{ fontWeight: 500, fontSize: '12px', color: 'var(--vscode-descriptionForeground)' }}>Pricing (USD per million tokens)</span>
|
|
|
+ <div style={{ display: "flex", flexDirection: "column", gap: 8, marginTop: 5 }}>
|
|
|
+ {/* Input/Output Prices */}
|
|
|
+ <div style={{ display: "flex", gap: 10 }}>
|
|
|
+ <VSCodeTextField
|
|
|
+ value={apiConfiguration?.openAiCusModelInfo?.inputPrice?.toString() || openAiModelInfoSaneDefaults.inputPrice?.toString() || ""}
|
|
|
+ type="text"
|
|
|
+ style={{ width: "100%" }}
|
|
|
+ title="Cost per million tokens in the input/prompt"
|
|
|
+ onChange={(e: any) => {
|
|
|
+ const parsed = parseFloat(e.target.value)
|
|
|
+ setApiConfiguration({
|
|
|
+ ...apiConfiguration,
|
|
|
+ openAiCusModelInfo: {
|
|
|
+ ...(apiConfiguration?.openAiCusModelInfo || openAiModelInfoSaneDefaults),
|
|
|
+ inputPrice: e.target.value === "" ? undefined : (isNaN(parsed) ? openAiModelInfoSaneDefaults.inputPrice : parsed)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }}
|
|
|
+ placeholder="e.g. 0.0001">
|
|
|
+ <span style={{ fontWeight: 500 }}>Input Price</span>
|
|
|
+ </VSCodeTextField>
|
|
|
+
|
|
|
+ <VSCodeTextField
|
|
|
+ value={apiConfiguration?.openAiCusModelInfo?.outputPrice?.toString() || openAiModelInfoSaneDefaults.outputPrice?.toString() || ""}
|
|
|
+ type="text"
|
|
|
+ style={{ width: "100%" }}
|
|
|
+ title="Cost per million tokens in the model's response"
|
|
|
+ onChange={(e: any) => {
|
|
|
+ const parsed = parseFloat(e.target.value)
|
|
|
+ setApiConfiguration({
|
|
|
+ ...apiConfiguration,
|
|
|
+ openAiCusModelInfo: {
|
|
|
+ ...(apiConfiguration?.openAiCusModelInfo || openAiModelInfoSaneDefaults),
|
|
|
+ outputPrice: e.target.value === "" ? undefined : (isNaN(parsed) ? openAiModelInfoSaneDefaults.outputPrice : parsed)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }}
|
|
|
+ placeholder="e.g. 0.0002">
|
|
|
+ <span style={{ fontWeight: 500 }}>Output Price</span>
|
|
|
+ </VSCodeTextField>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* Cache Prices */}
|
|
|
+ <div style={{ display: "flex", gap: 10 }}>
|
|
|
+ <VSCodeTextField
|
|
|
+ value={apiConfiguration?.openAiCusModelInfo?.cacheWritesPrice?.toString() || openAiModelInfoSaneDefaults.cacheWritesPrice?.toString() || ""}
|
|
|
+ type="text"
|
|
|
+ style={{ width: "100%" }}
|
|
|
+ title="Cost per million tokens when writing to the prompt cache"
|
|
|
+ onChange={(e: any) => {
|
|
|
+ const parsed = parseFloat(e.target.value)
|
|
|
+ setApiConfiguration({
|
|
|
+ ...apiConfiguration,
|
|
|
+ openAiCusModelInfo: {
|
|
|
+ ...(apiConfiguration?.openAiCusModelInfo || openAiModelInfoSaneDefaults),
|
|
|
+ cacheWritesPrice: e.target.value === "" ? undefined : (isNaN(parsed) ? openAiModelInfoSaneDefaults.cacheWritesPrice : parsed)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }}
|
|
|
+ placeholder="e.g. 0.0001">
|
|
|
+ <span style={{ fontWeight: 500 }}>Cache Write Price</span>
|
|
|
+ </VSCodeTextField>
|
|
|
+
|
|
|
+ <VSCodeTextField
|
|
|
+ value={apiConfiguration?.openAiCusModelInfo?.cacheReadsPrice?.toString() || openAiModelInfoSaneDefaults.cacheReadsPrice?.toString() || ""}
|
|
|
+ type="text"
|
|
|
+ style={{ width: "100%" }}
|
|
|
+ title="Cost per million tokens when reading from the prompt cache"
|
|
|
+ onChange={(e: any) => {
|
|
|
+ const parsed = parseFloat(e.target.value)
|
|
|
+ setApiConfiguration({
|
|
|
+ ...apiConfiguration,
|
|
|
+ openAiCusModelInfo: {
|
|
|
+ ...(apiConfiguration?.openAiCusModelInfo || openAiModelInfoSaneDefaults),
|
|
|
+ cacheReadsPrice: e.target.value === "" ? undefined : (isNaN(parsed) ? openAiModelInfoSaneDefaults.cacheReadsPrice : parsed)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }}
|
|
|
+ placeholder="e.g. 0.00001">
|
|
|
+ <span style={{ fontWeight: 500 }}>Cache Read Price</span>
|
|
|
+ </VSCodeTextField>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ { /* TODO: model info here */}
|
|
|
+
|
|
|
+
|
|
|
<p
|
|
|
style={{
|
|
|
fontSize: "12px",
|
|
|
@@ -1031,7 +1209,7 @@ export function normalizeApiConfiguration(apiConfiguration?: ApiConfiguration) {
|
|
|
return {
|
|
|
selectedProvider: provider,
|
|
|
selectedModelId: apiConfiguration?.openAiModelId || "",
|
|
|
- selectedModelInfo: openAiModelInfoSaneDefaults,
|
|
|
+ selectedModelInfo: apiConfiguration?.openAiCusModelInfo || openAiModelInfoSaneDefaults,
|
|
|
}
|
|
|
case "ollama":
|
|
|
return {
|