Repository: kaito-project/kaito

Issue:
Add gpt-oss-20b and gpt-oss-120b as KAITO Presets
**Is your feature request related to a problem? Please describe.**

https://openai.com/open-models/

According to https://huggingface.co/openai/gpt-oss-120b, we need to use a pre-released version of vLLM so we might need to wait until the official release of 0.10.1.

```
uv pip install --pre vllm==0.10.1+gptoss \
    --extra-index-url https://wheels.vllm.ai/gpt-oss/ \
    --extra-index-url https://download.pytorch.org/whl/nightly/cu128 \
    --index-strategy unsafe-best-match

vllm serve openai/gpt-oss-120b
```

**Describe the solution you'd like**

**Describe alternatives you've considered**

**Additional context**

Discussion:
@sdesai345: While vLLM v0.10.1 awaits release, let's investigate an upgrade to HF Transformers v4.55 to support gpt-oss as presets.@sdesai345: Update: https://github.com/vllm-project/vllm/releases/tag/v0.10.1 pre-release is out

Solution patch:
diff --git a/.github/e2e-preset-configs.json b/.github/e2e-preset-configs.json
index 12a2c40..553c67a 100644
--- a/.github/e2e-preset-configs.json
+++ b/.github/e2e-preset-configs.json
@@ -326,6 +326,36 @@
           }
         }
       },
+      {
+        "name": "gpt-oss-20b",
+        "node-count": 1,
+        "node-vm-size": "Standard_NV36ads_A10_v5",
+        "node-osdisk-size": 120,
+        "OSS": true,
+        "loads_adapter": false,
+        "node_pool": "gptoss20b",
+        "runtimes": {
+          "hf": {
+            "command": "accelerate launch --num_processes 1 --num_machines 1 --machine_rank 0 --gpu_ids all /workspace/tfs/inference_api.py --pipeline text-generation --torch_dtype auto --allow_remote_files",
+            "gpu_count": 1
+          }
+        }
+      },
+      {
+        "name": "gpt-oss-120b",
+        "node-count": 1,
+        "node-vm-size": "Standard_NC24ads_A100_v4",
+        "node-osdisk-size": 200,
+        "OSS": true,
+        "loads_adapter": false,
+        "node_pool": "gptoss120b",
+        "runtimes": {
+          "hf": {
+            "command": "accelerate launch --num_processes 1 --num_machines 1 --machine_rank 0 --gpu_ids all /workspace/tfs/inference_api.py --pipeline text-generation --torch_dtype auto --allow_remote_files",
+            "gpu_count": 1
+          }
+        }
+      },
       {
         "name": "vllm-distributed",
         "node-count": 2,
diff --git a/.github/workflows/publish-rag-controller-mcr-image.yaml b/.github/workflows/publish-rag-controller-mcr-image.yaml
index 0459233..d353bb4 100644
--- a/.github/workflows/publish-rag-controller-mcr-image.yaml
+++ b/.github/workflows/publish-rag-controller-mcr-image.yaml
@@ -1,5 +1,10 @@
 name: Publish RAGEngine MCR image
 on:
+  workflow_dispatch:
+    inputs:
+      tag:
+        description: 'tag to be created for this image (e.g. v0.6.0)'
+        required: true
   repository_dispatch:
     types: [ publish-rag-controller-mcr-image ]
 
@@ -24,14 +29,14 @@ jobs:
 
       - name: Set Image tag
         run: |
-          ver=${{ github.event.client_payload.tag }}
+          ver=${{ github.event.client_payload.tag || github.event.inputs.tag }}
           echo "IMG_TAG=${ver#"v"}" >> $GITHUB_ENV
 
       - uses: actions/checkout@v5.0.0
         with:
           fetch-depth: 0
           submodules: true
-          ref: ${{ github.event.client_payload.tag }}
+          ref: ${{ github.event.client_payload.tag || github.event.inputs.tag }}
 
       - name: Authenticate to ACR
         run: |
@@ -43,7 +48,7 @@ jobs:
         run: |
           OUTPUT_TYPE=type=registry make docker-build-ragengine
         env:
-          VERSION: ${{ github.event.client_payload.tag }}
+          VERSION: ${{ github.event.client_payload.tag || github.event.inputs.tag }}
           REGISTRY: ${{ secrets.KAITO_MCR_REGISTRY }}/public/aks/kaito
           RAGENGINE_IMAGE_NAME: ragengine
           IMG_TAG: ${{ env.IMG_TAG }}
@@ -57,4 +62,4 @@ jobs:
         with:
           token: ${{ secrets.GITHUB_TOKEN }}
           event-type: publish-ragengine-helm-chart
-          client-payload: '{"tag": "${{ github.event.client_payload.tag }}"}'
+          client-payload: '{"tag": "${{ github.event.client_payload.tag || github.event.inputs.tag }}"}'
diff --git a/.github/workflows/publish-rag-service-mcr-image.yaml b/.github/workflows/publish-rag-service-mcr-image.yaml
index 552076a..9a9b7c5 100644
--- a/.github/workflows/publish-rag-service-mcr-image.yaml
+++ b/.github/workflows/publish-rag-service-mcr-image.yaml
@@ -1,5 +1,10 @@
 name: Publish RAG Service MCR image
 on:
+  workflow_dispatch:
+    inputs:
+      tag:
+        description: 'tag to be created for this image (e.g. v0.6.0)'
+        required: true
   repository_dispatch:
     types: [ publish-rag-service-mcr-image ]
 
@@ -24,14 +29,14 @@ jobs:
 
       - name: Set Image tag
         run: |
-          ver=${{ github.event.client_payload.tag }}
+          ver=${{ github.event.client_payload.tag || github.event.inputs.tag }}
           echo "IMG_TAG=${ver#"v"}" >> $GITHUB_ENV
 
       - uses: actions/checkout@v5.0.0
         with:
           fetch-depth: 0
           submodules: true
-          ref: ${{ github.event.client_payload.tag }}
+          ref: ${{ github.event.client_payload.tag || github.event.inputs.tag }}
 
       - name: Authenticate to ACR
         run: |
@@ -43,7 +48,7 @@ jobs:
         run: |
           OUTPUT_TYPE=type=registry make docker-build-ragservice
         env:
-          VERSION: ${{ github.event.client_payload.tag }}
+          VERSION: ${{ github.event.client_payload.tag || github.event.inputs.tag }}
           REGISTRY: ${{ secrets.KAITO_MCR_REGISTRY }}/unlisted/aks/kaito
           RAGENGINE_SERVICE_IMG_NAME: kaito-rag-service
           RAGENGINE_SERVICE_IMG_TAG: ${{ env.IMG_TAG }}
@@ -57,4 +62,4 @@ jobs:
         with:
           token: ${{ secrets.GITHUB_TOKEN }}
           event-type: publish-ragengine-helm-chart
-          client-payload: '{"tag": "${{ github.event.client_payload.tag }}"}'
+          client-payload: '{"tag": "${{ github.event.client_payload.tag || github.event.inputs.tag }}"}'
diff --git a/.github/workflows/publish-workspace-mcr-image.yaml b/.github/workflows/publish-workspace-mcr-image.yaml
index 436b51e..f161617 100644
--- a/.github/workflows/publish-workspace-mcr-image.yaml
+++ b/.github/workflows/publish-workspace-mcr-image.yaml
@@ -1,5 +1,10 @@
 name: Publish KAITO MCR image
 on:
+  workflow_dispatch:
+    inputs:
+      tag:
+        description: 'tag to be created for this image (e.g. v0.6.0)'
+        required: true
   repository_dispatch:
     types: [ publish-workspace-mcr-image ]
 
@@ -24,14 +29,14 @@ jobs:
 
       - name: Set Image tag
         run: |
-          ver=${{ github.event.client_payload.tag }}
+          ver=${{ github.event.client_payload.tag || github.event.inputs.tag }}
           echo "IMG_TAG=${ver#"v"}" >> $GITHUB_ENV
 
       - uses: actions/checkout@v5.0.0
         with:
           fetch-depth: 0
           submodules: true
-          ref: ${{ github.event.client_payload.tag }}
+          ref: ${{ github.event.client_payload.tag || github.event.inputs.tag }}
 
       - name: Authenticate to ACR
         run: |
@@ -43,7 +48,7 @@ jobs:
         run: |
           OUTPUT_TYPE=type=registry make docker-build-workspace
         env:
-          VERSION: ${{ github.event.client_payload.tag }}
+          VERSION: ${{ github.event.client_payload.tag || github.event.inputs.tag }}
           REGISTRY: ${{ secrets.KAITO_MCR_REGISTRY }}/public/aks/kaito
           IMG_NAME: workspace
           IMG_TAG: ${{ env.IMG_TAG }}
@@ -57,4 +62,4 @@ jobs:
         with:
           token: ${{ secrets.GITHUB_TOKEN }}
           event-type: publish-workspace-helm-chart
-          client-payload: '{"tag": "${{ github.event.client_payload.tag }}"}'
+          client-payload: '{"tag": "${{ github.event.client_payload.tag || github.event.inputs.tag }}"}'
diff --git a/Tiltfile b/Tiltfile
index bc8ec36..89e2709 100644
--- a/Tiltfile
+++ b/Tiltfile
@@ -73,7 +73,7 @@ def main(IMG='controller:latest', DISABLE_SECURITY_CONTEXT=True):
     # Generate the CRD manifests and kubectl apply them. Re-generate if anything in deps changes.
     local_resource(
         'crd',
-        make_generate() + ' && ' + make_manifests() + ' && kubectl apply --server-side -f charts/kaito/workspace/crds',
+        make_generate() + ' && ' + make_manifests() + ' && kubectl apply --server-side -f charts/kaito/workspace/crds --force-conflicts',
         deps=['api'],
         ignore=['*/*/zz_generated.deepcopy.go'],
         labels='Workspace',
diff --git a/charts/kaito/workspace/templates/supported-models-configmap.yaml b/charts/kaito/workspace/templates/supported-models-configmap.yaml
index b5694e0..e94ec1c 100644
--- a/charts/kaito/workspace/templates/supported-models-configmap.yaml
+++ b/charts/kaito/workspace/templates/supported-models-configmap.yaml
@@ -124,3 +124,14 @@ data:
         version: https://huggingface.co/deepseek-ai/DeepSeek-R1-Distill-Llama-8B/commit/6a6f4aa4197940add57724a7707d069478df56b1
         runtime: tfs
         tag: 0.2.0
+      - name: gpt-oss-20b
+        type: text-generation
+        version: https://huggingface.co/openai/gpt-oss-20b/commit/db6852caddef0cf0c1f729034ed700c84eab376e
+        runtime: tfs
+        tag: 0.0.1
+      - name: gpt-oss-120b
+        type: text-generation
+        version: https://huggingface.co/openai/gpt-oss-120b/commit/b5c939de8f754692c1647ca79fbf85e8c1e70f8a
+        runtime: tfs
+        downloadAtRuntime: true
+        tag: 0.0.1
diff --git a/cmd/workspace/models.go b/cmd/workspace/models.go
index b53b60c..73cc007 100644
--- a/cmd/workspace/models.go
+++ b/cmd/workspace/models.go
@@ -17,6 +17,7 @@ import (
 	_ "github.com/kaito-project/kaito/presets/workspace/models"
 	_ "github.com/kaito-project/kaito/presets/workspace/models/deepseek"
 	_ "github.com/kaito-project/kaito/presets/workspace/models/falcon"
+	_ "github.com/kaito-project/kaito/presets/workspace/models/gpt"
 	_ "github.com/kaito-project/kaito/presets/workspace/models/llama3"
 	_ "github.com/kaito-project/kaito/presets/workspace/models/mistral"
 	_ "github.com/kaito-project/kaito/presets/workspace/models/phi2"
diff --git a/docs/proposals/20250808-gpt-oss-20b.md b/docs/proposals/20250808-gpt-oss-20b.md
new file mode 100644
index 0000000..415e86d
--- /dev/null
+++ b/docs/proposals/20250808-gpt-oss-20b.md
@@ -0,0 +1,68 @@
+---
+title: Proposal for new model support
+authors:
+  - "@chewong"
+reviewers:
+  - "KAITO contributor"
+creation-date: 2025-08-08
+last-updated: 2025-08-08
+status: provisional
+---
+
+# Title
+
+Add [openai/gpt-oss-20b](https://huggingface.co/openai/gpt-oss-20b) to KAITO supported model list
+
+## Glossary
+
+N/A
+
+## Summary
+
+- Model description: GPT-OSS 20B is an open-weight large language model from OpenAI designed for strong reasoning, function-calling, and agentic workflows. See the official model card linked above and blog announcement at https://openai.com/index/introducing-gpt-oss/.
+- Model usage statistics: N/A
+- Model license: Apache-2.0.
+
+Samepl YAML:
+
+```yaml
+apiVersion: kaito.sh/v1beta1
+kind: Workspace
+metadata:
+  name: workspace-gpt-oss-20b
+resource:
+  instanceType: Standard_NV36ads_A10_v5
+  labelSelector:
+    matchLabels:
+      apps: gpt-oss-20b
+```
+## Requirements
+The following table describes the basic model characteristics and the resource requirements of running it.
+| Field            | Notes                                                                                                |
+| ---------------- | ---------------------------------------------------------------------------------------------------- |
+| Family name      | gpt-oss                                                                                              |
+| Type             | `text-generation`                                                                                    |
+| Download site    | https://huggingface.co/openai/gpt-oss-20b                                                            |
+| Version          | [db6852c](https://huggingface.co/openai/gpt-oss-20b/commit/db6852caddef0cf0c1f729034ed700c84eab376e) |
+| Storage size     | ~16GB                                                                                                |
+| GPU count        | 1                                                                                                    |
+| Total GPU memory | ~16GB (per https://openai.com/index/introducing-gpt-oss/)                                            |
+| Per GPU memory   | N/A                                                                                                  |
+
+## Runtimes
+
+This section describes how to configure the runtime framework to support the inference calls.
+
+| Options               | Notes                                                                                                                  |
+| --------------------- | ---------------------------------------------------------------------------------------------------------------------- |
+| Runtime               | HuggingFace Transformers. vLLM support will be available once v0.10.1 is released.                                     |
+| Distributed Inference | False                                                                                                                  |
+| Custom configurations | Kaito enables `torch_dtype=auto` and `allow_remote_files` by default to use the model’s chat template when applicable. |
+
+Note: vLLM is not supported for gpt-oss-20b at this time.
+
+# History
+
+- [x] 2025-08-08: Open proposal PR.
+- [x] 2025-08-08: Start model integration.
+- [ ] 2025-08-08: Complete model support.
\ No newline at end of file
diff --git a/examples/inference/kaito_workspace_gpt_oss_120b.yaml b/examples/inference/kaito_workspace_gpt_oss_120b.yaml
new file mode 100644
index 0000000..33932a9
--- /dev/null
+++ b/examples/inference/kaito_workspace_gpt_oss_120b.yaml
@@ -0,0 +1,12 @@
+apiVersion: kaito.sh/v1beta1
+kind: Workspace
+metadata:
+  name: workspace-gpt-oss-120b
+resource:
+  instanceType: "Standard_NC24ads_A100_v4"
+  labelSelector:
+    matchLabels:
+      apps: gpt-oss-120b
+inference:
+  preset:
+    name: "gpt-oss-120b"
diff --git a/examples/inference/kaito_workspace_gpt_oss_20b.yaml b/examples/inference/kaito_workspace_gpt_oss_20b.yaml
new file mode 100644
index 0000000..de26fc4
--- /dev/null
+++ b/examples/inference/kaito_workspace_gpt_oss_20b.yaml
@@ -0,0 +1,22 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: inference-params
+data:
+  inference_config.yaml: |
+    vllm:
+      gpu-memory-utilization: 0.82  # Controls GPU memory usage (0.0-1.0)
+---
+apiVersion: kaito.sh/v1beta1
+kind: Workspace
+metadata:
+  name: workspace-gpt-oss-20b
+resource:
+  instanceType: "Standard_NV36ads_A10_v5"
+  labelSelector:
+    matchLabels:
+      apps: gpt-oss-20b
+inference:
+  preset:
+    name: "gpt-oss-20b"
+  config: "inference-params"  # Reference to ConfigMap name
diff --git a/presets/workspace/models/gpt/README.md b/presets/workspace/models/gpt/README.md
new file mode 100644
index 0000000..bd23e04
--- /dev/null
+++ b/presets/workspace/models/gpt/README.md
@@ -0,0 +1,12 @@
+## Supported Models
+| Model name  |                      Model source                       |                               Sample workspace                                | Kubernetes Workload | Distributed inference |
+|-------------|:-------------------------------------------------------:|:-----------------------------------------------------------------------------:|:-------------------:|:---------------------:|
+| gpt-oss-20b | [openai](https://huggingface.co/openai/gpt-oss-20b)   | [link](../../../../examples/inference/kaito_workspace_gpt_oss_20b.yaml)      |     Deployment      |         true          |
+| gpt-oss-120b| [openai](https://huggingface.co/openai/gpt-oss-120b)  | [link](../../../../examples/inference/kaito_workspace_gpt_oss_120b.yaml)     |     Deployment      |         true          |
+
+## Image Source
+- **Public**: KAITO maintainers manage the lifecycle of the inference service images that contain model weights. The images are available in Microsoft Container Registry (MCR).
+
+## Usage
+
+See [document](../../../../docs/inference/README.md).
diff --git a/presets/workspace/models/gpt/model.go b/presets/workspace/models/gpt/model.go
new file mode 100644
index 0000000..95260b2
--- /dev/null
+++ b/presets/workspace/models/gpt/model.go
@@ -0,0 +1,135 @@
+// Copyright (c) KAITO authors.
+// Licensed 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.
+
+package gpt
+
+import (
+	"time"
+
+	"github.com/kaito-project/kaito/pkg/model"
+	"github.com/kaito-project/kaito/pkg/utils/plugin"
+	"github.com/kaito-project/kaito/pkg/workspace/inference"
+	metadata "github.com/kaito-project/kaito/presets/workspace/models"
+)
+
+func init() {
+	plugin.KaitoModelRegister.Register(&plugin.Registration{
+		Name:     PresetGPT_OSS_20BModel,
+		Instance: &gptOss20b,
+	})
+	plugin.KaitoModelRegister.Register(&plugin.Registration{
+		Name:     PresetGPT_OSS_120BModel,
+		Instance: &gptOss120b,
+	})
+}
+
+const (
+	// PresetGPT_OSS_20BModel is the preset name matching supported_models.yaml.
+	PresetGPT_OSS_20BModel = "gpt-oss-20b"
+	// PresetGPT_OSS_120BModel is the preset name matching supported_models.yaml.
+	PresetGPT_OSS_120BModel = "gpt-oss-120b"
+)
+
+var (
+	baseCommandPresetGPTInference = "accelerate launch"
+	// GPT-OSS uses the Harmony chat format and provides its own chat template in the repo.
+	// We enable allow_remote_files so Transformers can fetch it when needed.
+	gptRunParams = map[string]string{
+		"torch_dtype":        "auto",
+		"pipeline":           "text-generation",
+		"allow_remote_files": "",
+	}
+	gptRunParamsVLLM = map[string]string{
+		"dtype": "bfloat16",
+	}
+)
+
+var gptOss20b gpt_oss_20B
+var gptOss120b gpt_oss_120B
+
+type gpt_oss_20B struct{}
+
+func (*gpt_oss_20B) GetInferenceParameters() *model.PresetParam {
+	return &model.PresetParam{
+		Metadata:               metadata.MustGet(PresetGPT_OSS_20BModel),
+		DiskStorageRequirement: "110Gi",
+		GPUCountRequirement:    "1",
+		// TotalGPUMemoryRequirement: "16Gi", // per https://openai.com/index/introducing-gpt-oss/
+		TotalGPUMemoryRequirement: "24Gi", // TODO: pod failed with out of memory error on A10 with 24 GB memory.
+		PerGPUMemoryRequirement:   "0Gi",  // Native vertical model parallel; no per-GPU split requirement
+		RuntimeParam: model.RuntimeParam{
+			Transformers: model.HuggingfaceTransformersParam{
+				BaseCommand:       baseCommandPresetGPTInference,
+				AccelerateParams:  inference.DefaultAccelerateParams,
+				InferenceMainFile: inference.DefaultTransformersMainFile,
+				ModelRunParams:    gptRunParams,
+			},
+			VLLM: model.VLLMParam{
+				BaseCommand:    inference.DefaultVLLMCommand,
+				ModelName:      PresetGPT_OSS_20BModel,
+				ModelRunParams: gptRunParamsVLLM,
+			},
+		},
+		ReadinessTimeout: time.Duration(30) * time.Minute,
+	}
+}
+
+func (*gpt_oss_20B) GetTuningParameters() *model.PresetParam {
+	return nil
+}
+
+func (*gpt_oss_20B) SupportDistributedInference() bool {
+	return false
+}
+
+func (*gpt_oss_20B) SupportTuning() bool {
+	return false
+}
+
+type gpt_oss_120B struct{}
+
+func (*gpt_oss_120B) GetInferenceParameters() *model.PresetParam {
+	return &model.PresetParam{
+		Metadata:                  metadata.MustGet(PresetGPT_OSS_120BModel),
+		DiskStorageRequirement:    "250Gi", // Larger model needs more disk space
+		GPUCountRequirement:       "1",
+		TotalGPUMemoryRequirement: "80Gi", // Single 80GB GPU requirement
+		PerGPUMemoryRequirement:   "0Gi",  // Native vertical model parallel; no per-GPU split requirement
+		RuntimeParam: model.RuntimeParam{
+			Transformers: model.HuggingfaceTransformersParam{
+				BaseCommand:       baseCommandPresetGPTInference,
+				AccelerateParams:  inference.DefaultAccelerateParams,
+				InferenceMainFile: inference.DefaultTransformersMainFile,
+				ModelRunParams:    gptRunParams,
+			},
+			VLLM: model.VLLMParam{
+				BaseCommand:    inference.DefaultVLLMCommand,
+				ModelName:      PresetGPT_OSS_120BModel,
+				ModelRunParams: gptRunParamsVLLM,
+			},
+		},
+		ReadinessTimeout: time.Duration(45) * time.Minute, // Longer timeout for larger model
+	}
+}
+
+func (*gpt_oss_120B) GetTuningParameters() *model.PresetParam {
+	return nil
+}
+
+func (*gpt_oss_120B) SupportDistributedInference() bool {
+	return false
+}
+
+func (*gpt_oss_120B) SupportTuning() bool {
+	return false
+}
diff --git a/presets/workspace/models/supported_models.yaml b/presets/workspace/models/supported_models.yaml
index 0fcb627..d399729 100644
--- a/presets/workspace/models/supported_models.yaml
+++ b/presets/workspace/models/supported_models.yaml
@@ -226,4 +226,19 @@ models:
     # 0.2.0 - Convert to individual OCI artifacts
     # 0.1.1 - bump vLLM to 0.8.5
     # 0.1.0 - vLLM, Transformers, and several other library updates and fixes
-    # 0.0.1 - New Model!
\ No newline at end of file
+    # 0.0.1 - New Model!
+
+  # GPT-OSS
+  - name: gpt-oss-20b
+    type: text-generation
+    version: https://huggingface.co/openai/gpt-oss-20b/commit/db6852caddef0cf0c1f729034ed700c84eab376e
+    runtime: tfs
+    tag: 0.0.1
+  - name: gpt-oss-120b
+    type: text-generation
+    version: https://huggingface.co/openai/gpt-oss-120b/commit/b5c939de8f754692c1647ca79fbf85e8c1e70f8a
+    runtime: tfs
+    downloadAtRuntime: true
+    tag: 0.0.1
+    # Tag history:
+    # 0.0.1 - Initial Release



Test patch:
diff --git a/test/e2e/preset_test.go b/test/e2e/preset_test.go
index 9649402..039b117 100644
--- a/test/e2e/preset_test.go
+++ b/test/e2e/preset_test.go
@@ -28,7 +28,6 @@ import (
 	appsv1 "k8s.io/api/apps/v1"
 	batchv1 "k8s.io/api/batch/v1"
 	corev1 "k8s.io/api/core/v1"
-	v1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/api/resource"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -51,6 +50,8 @@ const (
 	PresetDeepSeekR1DistillLlama8BModel = "deepseek-r1-distill-llama-8b"
 	PresetDeepSeekR1DistillQwen14BModel = "deepseek-r1-distill-qwen-14b"
 	PresetPhi4MiniModel                 = "phi-4-mini-instruct"
+	PresetGPT_OSS_20BModel              = "gpt-oss-20b"
+	PresetGPT_OSS_120BModel             = "gpt-oss-120b"
 	WorkspaceHashAnnotation             = "workspace.kaito.io/hash"
 	// WorkspaceRevisionAnnotation represents the revision number of the workload managed by the workspace
 	WorkspaceRevisionAnnotation = "workspace.kaito.io/revision"
@@ -210,7 +211,21 @@ func createPhi3WorkspaceWithPresetPublicMode(numOfNode int) *kaitov1beta1.Worksp
 	return workspaceObj
 }
 
-func createCustomTuningConfigMapForE2E() *v1.ConfigMap {
+func createGPTOss120BWorkspaceWithPresetPublicMode(numOfNode int) *kaitov1beta1.Workspace {
+	workspaceObj := &kaitov1beta1.Workspace{}
+	By("Creating a workspace CR with GPT-OSS-120B preset public mode", func() {
+		uniqueID := fmt.Sprint("preset-gpt-oss-120b-", rand.Intn(1000))
+		workspaceObj = utils.GenerateInferenceWorkspaceManifest(uniqueID, namespaceName, "",
+			numOfNode, "Standard_NC24ads_A100_v4", &metav1.LabelSelector{
+				MatchLabels: map[string]string{"kaito-workspace": "public-preset-e2e-test-gpt-oss-120b"},
+			}, nil, PresetGPT_OSS_120BModel, nil, nil, nil, "")
+
+		createAndValidateWorkspace(workspaceObj)
+	})
+	return workspaceObj
+}
+
+func createCustomTuningConfigMapForE2E() *corev1.ConfigMap {
 	configMap := utils.GenerateE2ETuningConfigMapManifest(namespaceName)
 
 	By("Creating a custom workspace tuning configmap for E2E", func() {
@@ -220,7 +235,7 @@ func createCustomTuningConfigMapForE2E() *v1.ConfigMap {
 	return configMap
 }
 
-func createAndValidateConfigMap(configMap *v1.ConfigMap) {
+func createAndValidateConfigMap(configMap *corev1.ConfigMap) {
 	By("Creating ConfigMap", func() {
 		Eventually(func() error {
 			err := utils.TestingCluster.KubeClient.Create(ctx, configMap, &client.CreateOptions{})
@@ -275,8 +290,13 @@ func createPhi3TuningWorkspaceWithPresetPublicMode(configMapName string, numOfNo
 	return workspaceObj, uniqueID, outputRegistryUrl
 }
 
-func createAndValidateWorkspace(workspaceObj *kaitov1beta1.Workspace) {
-	createConfigForWorkspace(workspaceObj)
+func createAndValidateWorkspace(workspaceObj *kaitov1beta1.Workspace, configMapData ...map[string]string) {
+	var customConfigData map[string]string
+	if len(configMapData) > 0 && configMapData[0] != nil {
+		customConfigData = configMapData[0]
+	}
+	createConfigForWorkspace(workspaceObj, customConfigData)
+
 	By("Creating workspace", func() {
 		Eventually(func() error {
 			return utils.TestingCluster.KubeClient.Create(ctx, workspaceObj, &client.CreateOptions{})
@@ -293,7 +313,7 @@ func createAndValidateWorkspace(workspaceObj *kaitov1beta1.Workspace) {
 	})
 }
 
-func createConfigForWorkspace(workspaceObj *kaitov1beta1.Workspace) {
+func createConfigForWorkspace(workspaceObj *kaitov1beta1.Workspace, customConfigData map[string]string) {
 	if workspaceObj.Inference == nil || workspaceObj.Resource.InstanceType == "" {
 		return
 	}
@@ -306,17 +326,26 @@ func createConfigForWorkspace(workspaceObj *kaitov1beta1.Workspace) {
 	// }
 
 	By("Creating config file", func() {
-		cm := corev1.ConfigMap{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "inference-config",
-				Namespace: workspaceObj.Namespace,
-			},
-			Data: map[string]string{
+		var configData map[string]string
+
+		// Use custom config data if provided, otherwise use default
+		if customConfigData != nil {
+			configData = customConfigData
+		} else {
+			configData = map[string]string{
 				"inference_config.yaml": `
 vllm:
   max-model-len: 1024
 `,
+			}
+		}
+
+		cm := corev1.ConfigMap{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      workspaceObj.Name + "-config",
+				Namespace: workspaceObj.Namespace,
 			},
+			Data: configData,
 		}
 		workspaceObj.Inference.Config = cm.Name
 
@@ -377,7 +406,7 @@ func updateAndValidateWorkspace(workspaceObj *kaitov1beta1.Workspace) {
 
 func copySecretToNamespace(secretName, targetNamespace string) error {
 	originalNamespace := "default"
-	originalSecret := &v1.Secret{}
+	originalSecret := &corev1.Secret{}
 
 	// Fetch the original secret from the default namespace
 	err := utils.TestingCluster.KubeClient.Get(ctx, client.ObjectKey{
@@ -427,7 +456,7 @@ func validateAssociatedService(workspaceObj *kaitov1beta1.Workspace) {
 	serviceNamespace := workspaceObj.Namespace
 
 	By(fmt.Sprintf("Checking for service %s in namespace %s", serviceName, serviceNamespace), func() {
-		service := &v1.Service{}
+		service := &corev1.Service{}
 
 		Eventually(func() bool {
 			err := utils.TestingCluster.KubeClient.Get(ctx, client.ObjectKey{
@@ -613,7 +642,7 @@ func validateTuningJobInputOutput(workspaceObj *kaitov1beta1.Workspace, inputIma
 					return false
 				}
 			} else {
-				var pullerContainer *v1.Container
+				var pullerContainer *corev1.Container
 				for _, container := range job.Spec.Template.Spec.InitContainers {
 					if strings.HasPrefix(container.Name, "puller") {
 						pullerContainer = &container
@@ -640,7 +669,7 @@ func validateTuningJobInputOutput(workspaceObj *kaitov1beta1.Workspace, inputIma
 					return false
 				}
 			} else {
-				var pusherContainer *v1.Container
+				var pusherContainer *corev1.Container
 				for _, container := range job.Spec.Template.Spec.Containers {
 					if strings.HasPrefix(container.Name, "pusher") {
 						pusherContainer = &container
@@ -1178,6 +1207,26 @@ var _ = Describe("Workspace Preset", func() {
 		validateWorkspaceReadiness(workspaceObj)
 	})
 
+	It("should create a gpt-oss-120b workspace with preset public mode successfully", utils.GinkgoLabelA100Required, func() {
+		numOfNode := 1
+		workspaceObj := createGPTOss120BWorkspaceWithPresetPublicMode(numOfNode)
+
+		defer cleanupResources(workspaceObj)
+		time.Sleep(30 * time.Second)
+
+		validateCreateNode(workspaceObj, numOfNode)
+		validateResourceStatus(workspaceObj)
+
+		time.Sleep(30 * time.Second)
+
+		validateAssociatedService(workspaceObj)
+		validateInferenceConfig(workspaceObj)
+
+		validateInferenceResource(workspaceObj, int32(numOfNode), false)
+
+		validateWorkspaceReadiness(workspaceObj)
+	})
+
 	It("should create a workspace for tuning successfully, and update the workspace with another dataset and output image", utils.GinkgoLabelFastCheck, func() {
 		numOfNode := 1
 		configMap := createCustomTuningConfigMapForE2E()
@@ -1266,7 +1315,7 @@ func validateCreateNode(workspaceObj *kaitov1beta1.Workspace, numOfNode int) {
 func validateInferenceConfig(workspaceObj *kaitov1beta1.Workspace) {
 	By("Checking the inference config exists", func() {
 		Eventually(func() bool {
-			configMap := &v1.ConfigMap{}
+			configMap := &corev1.ConfigMap{}
 			configName := kaitov1beta1.DefaultInferenceConfigTemplate
 			if workspaceObj.Inference.Config != "" {
 				configName = workspaceObj.Inference.Config
diff --git a/test/e2e/preset_vllm_test.go b/test/e2e/preset_vllm_test.go
index 21b2a04..436d31c 100644
--- a/test/e2e/preset_vllm_test.go
+++ b/test/e2e/preset_vllm_test.go
@@ -316,6 +316,52 @@ var _ = Describe("Workspace Preset on vllm runtime", func() {
 		validateCompletionsEndpoint(workspaceObj)
 		validateGatewayAPIInferenceExtensionResources(workspaceObj)
 	})
+
+	It("should create a gpt-oss-20b workspace with preset public mode successfully", utils.GinkgoLabelFastCheck, func() {
+		numOfNode := 1
+		workspaceObj := createGPTOss20BWorkspaceWithPresetPublicModeAndVLLM(numOfNode)
+
+		defer cleanupResources(workspaceObj)
+		time.Sleep(30 * time.Second)
+
+		validateCreateNode(workspaceObj, numOfNode)
+		validateResourceStatus(workspaceObj)
+
+		time.Sleep(30 * time.Second)
+
+		validateAssociatedService(workspaceObj)
+		validateInferenceConfig(workspaceObj)
+
+		validateInferenceResource(workspaceObj, int32(numOfNode), false)
+
+		validateWorkspaceReadiness(workspaceObj)
+		validateModelsEndpoint(workspaceObj)
+		validateCompletionsEndpoint(workspaceObj)
+		validateGatewayAPIInferenceExtensionResources(workspaceObj)
+	})
+
+	It("should create a gpt-oss-120b workspace with preset public mode successfully", utils.GinkgoLabelA100Required, func() {
+		numOfNode := 1
+		workspaceObj := createGPTOss120BWorkspaceWithPresetPublicModeAndVLLM(numOfNode)
+
+		defer cleanupResources(workspaceObj)
+		time.Sleep(30 * time.Second)
+
+		validateCreateNode(workspaceObj, numOfNode)
+		validateResourceStatus(workspaceObj)
+
+		time.Sleep(30 * time.Second)
+
+		validateAssociatedService(workspaceObj)
+		validateInferenceConfig(workspaceObj)
+
+		validateInferenceResource(workspaceObj, int32(numOfNode), false)
+
+		validateWorkspaceReadiness(workspaceObj)
+		validateModelsEndpoint(workspaceObj)
+		validateCompletionsEndpoint(workspaceObj)
+		validateGatewayAPIInferenceExtensionResources(workspaceObj)
+	})
 })
 
 func createDeepSeekLlama8BWorkspaceWithPresetPublicModeAndVLLM(numOfNode int) *kaitov1beta1.Workspace {
@@ -458,6 +504,43 @@ func createLlama3_3_70BInstructWorkspaceWithPresetPublicModeAndVLLM(numOfNode in
 	return workspaceObj
 }
 
+func createGPTOss20BWorkspaceWithPresetPublicModeAndVLLM(numOfNode int) *kaitov1beta1.Workspace {
+	workspaceObj := &kaitov1beta1.Workspace{}
+
+	By("Creating a workspace CR with GPT-OSS-20B preset public mode and vLLM", func() {
+		uniqueID := fmt.Sprint("preset-gpt-oss-20b-", rand.Intn(1000))
+		workspaceObj = utils.GenerateInferenceWorkspaceManifestWithVLLM(uniqueID, namespaceName, "", numOfNode, "Standard_NV36ads_A10_v5",
+			&metav1.LabelSelector{
+				MatchLabels: map[string]string{"kaito-workspace": "public-preset-e2e-test-gpt-oss-20b-vllm"},
+			}, nil, PresetGPT_OSS_20BModel, nil, nil, nil, "")
+
+		// Pass custom config data with gpu-memory-utilization and max-model-len
+		customConfigData := map[string]string{
+			"inference_config.yaml": `vllm:
+  gpu-memory-utilization: 0.82  # Controls GPU memory usage (0.0-1.0)
+  max-model-len: 1024`,
+		}
+
+		createAndValidateWorkspace(workspaceObj, customConfigData)
+	})
+
+	return workspaceObj
+}
+
+func createGPTOss120BWorkspaceWithPresetPublicModeAndVLLM(numOfNode int) *kaitov1beta1.Workspace {
+	workspaceObj := &kaitov1beta1.Workspace{}
+	By("Creating a workspace CR with GPT-OSS-120B preset public mode and vLLM", func() {
+		uniqueID := fmt.Sprint("preset-gpt-oss-120b-", rand.Intn(1000))
+		workspaceObj = utils.GenerateInferenceWorkspaceManifestWithVLLM(uniqueID, namespaceName, "", numOfNode, "Standard_NC24ads_A100_v4",
+			&metav1.LabelSelector{
+				MatchLabels: map[string]string{"kaito-workspace": "public-preset-e2e-test-gpt-oss-120b-vllm"},
+			}, nil, PresetGPT_OSS_120BModel, nil, nil, nil, "")
+
+		createAndValidateWorkspace(workspaceObj)
+	})
+	return workspaceObj
+}
+
 func validateGatewayAPIInferenceExtensionResources(workspaceObj *kaitov1beta1.Workspace) {
 	// Only validate if the Inference Preset is set
 	if workspaceObj.Inference.Preset == nil {