diff --git a/config/crd/bases/aiconnect.ansible.com_ansibleaiconnects.yaml b/config/crd/bases/aiconnect.ansible.com_ansibleaiconnects.yaml index 847b01fb..030439fc 100644 --- a/config/crd/bases/aiconnect.ansible.com_ansibleaiconnects.yaml +++ b/config/crd/bases/aiconnect.ansible.com_ansibleaiconnects.yaml @@ -61,6 +61,30 @@ spec: chatbot_image_version: description: Chatbot API container image tag or version to use type: string + chatbot_rag_db_image: + description: Registry path to Chatbot RAG database container image to use + type: string + chatbot_rag_db_image_version: + description: Chatbot RAG database container image tag or version to use + type: string + chatbot_mcp_gateway_image: + description: Registry path to AAP Gateway MCP server container image to use + type: string + chatbot_mcp_gateway_image_version: + description: AAP Gateway MCP server container image tag or version to use + type: string + chatbot_mcp_controller_image: + description: Registry path to AAP Controller MCP server container image to use + type: string + chatbot_mcp_controller_image_version: + description: AAP Controller MCP server container image tag or version to use + type: string + chatbot_mcp_lightspeed_image: + description: Registry path to AAP Lightspeed MCP server container image to use + type: string + chatbot_mcp_lightspeed_image_version: + description: AAP Lightspeed MCP server container image tag or version to use + type: string additional_labels: description: Additional labels defined on the resource, which should be propagated to child resources type: array diff --git a/config/manifests/bases/ansible-ai-connect-operator.clusterserviceversion.yaml b/config/manifests/bases/ansible-ai-connect-operator.clusterserviceversion.yaml index a1780f13..a2f1149b 100644 --- a/config/manifests/bases/ansible-ai-connect-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/ansible-ai-connect-operator.clusterserviceversion.yaml @@ -53,6 +53,38 @@ spec: path: chatbot_image_version x-descriptors: - urn:alm:descriptor:com.tectonic.ui:advanced + - displayName: Chatbot RAG database container image + path: chatbot_rag_db_image + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:advanced + - displayName: Chatbot RAG database container image Version + path: chatbot_rag_db_image_version + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:advanced + - displayName: AAP Gateway MCP server container image + path: chatbot_mcp_gateway_image + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:advanced + - displayName: AAP Gateway MCP server container image Version + path: chatbot_mcp_gateway_image_version + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:advanced + - displayName: AAP Controller MCP server container image + path: chatbot_mcp_controller_image + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:advanced + - displayName: AAP Controller MCP server container image Version + path: chatbot_mcp_controller_image_version + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:advanced + - displayName: AAP Lightspeed MCP server container image + path: chatbot_mcp_lightspeed_image + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:advanced + - displayName: AAP Lightspeed MCP server container image Version + path: chatbot_mcp_lightspeed_image_version + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:advanced - displayName: Additional labels defined on the resource, which should be propagated to child resources path: additional_labels diff --git a/docs/using-external-configuration-secrets.md b/docs/using-external-configuration-secrets.md index 8ec94f14..cb8f2eaa 100644 --- a/docs/using-external-configuration-secrets.md +++ b/docs/using-external-configuration-secrets.md @@ -88,10 +88,8 @@ stringData: chatbot_url: chatbot_model: chatbot_token: - chatbot_llm_provider_type: - chatbot_llm_provider_project_id: - chatbot_context_window_size: - chatbot_temperature_override: + aap_gateway_url: + aap_controller_url: type: Opaque ``` **Required Parameters** @@ -100,38 +98,21 @@ type: Opaque * `chatbot_model` * `chatbot_token` -**Optional Parameters** +**Parameter combinations** -Both `chatbot_llm_provider_type` and `chatbot_context_window_size` are optional. If either is omitted, the -following default values will be used: +Providing `aap_gateway_url` and/or `aap_controller_url` affect how the Chatbot is provisioned. -* `chatbot_llm_provider_type`: `rhoai_vllm` -* `chatbot_context_window_size`: `128000` +If none of these parameters are provided no MCP servers will be provisioned or registered with the underlying LLM's tool runtime. -When `chatbot_llm_provider_type` is set to `watsonx`, -`chatbot_llm_provider_project_id` is required to set to your watsonx.ai project ID. +If only `aap_gateway_url` is set the following MCP server will be provisioned: +- Ansible Lightspeed Service MCP server. + - Authentication will attempt to use the JWT token associated with the User's authenticated context. -`chatbot_temperature_override` is also optional. It is provided for -following Open AI models that do not support the default temperature setting used for -other Open AI models: - -* `o1` -* `o3-mini` -* `o4-mini` - -When one of these models is used, set `chatbot_temperature_override` to `null`, -which disables the default temperature setting. - - -**_Azure AI_ Parameters** - -In case of using the _Azure AI_ model provider, please also ensure the following required parameters are properly set: -* `chatbot_llm_provider_type`: `azure_openai` -* `chatbot_url`: `<>` -* `chatbot_model`: `<>` -* `chatbot_token`: `<>` -* `chatbot_azure_deployment_name`: `<>` -* `chatbot_azure_api_version`: `<>` +If `aap_gateway_url` and `aap_controller_url` are set the following MCP servers will be provisioned: +- AAP Controller Service MCP server + - Authentication will attempt to use either the JWT token associated with the User's authenticated context. +- Ansible Lightspeed Service MCP server + - Authentication will attempt to use either the JWT token associated with the User's authenticated context. ## Troubleshooting diff --git a/roles/chatbot/defaults/main.yml b/roles/chatbot/defaults/main.yml index 5832de59..e059fe3f 100644 --- a/roles/chatbot/defaults/main.yml +++ b/roles/chatbot/defaults/main.yml @@ -7,9 +7,21 @@ api_version: 'aiconnect.ansible.com/v1alpha1' deployment_type: 'ansible-ai-connect' -_chatbot_image: quay.io/ansible/ansible-chatbot-service +# Chatbot container image +_chatbot_image: quay.io/ansible/ansible-chatbot-stack _chatbot_image_version: "{{ lookup('env', 'DEFAULT_CHATBOT_AI_CONNECT_VERSION') or 'latest' }}" +# Chatbot RAG database container images +_chatbot_rag_db_image: quay.io/ansible/aap-rag-content +_chatbot_rag_db_image_version: "{{ lookup('env', 'DEFAULT_CHATBOT_RAG_DB_VERSION') or 'latest' }}" + +# Chatbot MCP Server container images +_chatbot_mcp_gateway_image: quay.io/ansible/ansible-mcp-gateway +_chatbot_mcp_gateway_image_version: "{{ lookup('env', 'DEFAULT_CHATBOT_MCP_GATEWAY_VERSION') or 'latest' }}" +_chatbot_mcp_controller_image: quay.io/ansible/ansible-mcp-controller +_chatbot_mcp_controller_image_version: "{{ lookup('env', 'DEFAULT_CHATBOT_MCP_CONTROLLER_VERSION') or 'latest' }}" +_chatbot_mcp_lightspeed_image: quay.io/ansible/ansible-mcp-lightspeed +_chatbot_mcp_lightspeed_image_version: "{{ lookup('env', 'DEFAULT_CHATBOT_MCP_LIGHTSPEED_VERSION') or 'latest' }}" # ======================================== diff --git a/roles/chatbot/tasks/deploy_chatbot_api.yml b/roles/chatbot/tasks/deploy_chatbot_api.yml index 0e2035bd..562af910 100644 --- a/roles/chatbot/tasks/deploy_chatbot_api.yml +++ b/roles/chatbot/tasks/deploy_chatbot_api.yml @@ -1,17 +1,15 @@ --- -- name: Apply Chatbot ConfigMap resources - kubernetes.core.k8s: - apply: yes - definition: "{{ lookup('template', 'chatbot.configmap.yaml.j2') }}" - wait: yes - - name: Apply Chatbot deployment resources kubernetes.core.k8s: apply: yes definition: "{{ lookup('template', item + '.yaml.j2') }}" wait: no loop: + - 'chatbot.pvc' - 'chatbot.service' + - 'chatbot.configmap_lightspeed_stack_config' + - 'chatbot.configmap_llama_stack_config' + - 'chatbot.configmap_system_prompt' - 'chatbot.deployment' - name: Check for Chatbot Pod @@ -30,7 +28,7 @@ - "chatbot_api_pod['resources'] | length" - "chatbot_api_pod['resources'][0]['status']['phase'] == 'Running'" - "chatbot_api_pod['resources'][0]['status']['containerStatuses'][0]['ready'] == true" - retries: 60 + retries: 30 delay: 5 - name: Set the Chatbot API Pod name as a variable. diff --git a/roles/chatbot/tasks/main.yml b/roles/chatbot/tasks/main.yml index bfc60b59..966079dc 100644 --- a/roles/chatbot/tasks/main.yml +++ b/roles/chatbot/tasks/main.yml @@ -8,6 +8,9 @@ - name: Set AnsibleAIConnect's Chatbot service images ansible.builtin.include_tasks: set_images.yml + - name: Set AnsibleAIConnect and AnsibleAIConnect's Chatbot service URLs + ansible.builtin.include_tasks: set_service_urls.yml + - name: Read AnsibleAIConnect's Chatbot secret ansible.builtin.include_tasks: read_chatbot_configuration_secret.yml diff --git a/roles/chatbot/tasks/read_chatbot_configuration_secret.yml b/roles/chatbot/tasks/read_chatbot_configuration_secret.yml index b5195675..dbd57e16 100644 --- a/roles/chatbot/tasks/read_chatbot_configuration_secret.yml +++ b/roles/chatbot/tasks/read_chatbot_configuration_secret.yml @@ -43,82 +43,28 @@ You must specify a 'chatbot_token' in your Secret. when: not _chatbot_config_resource["resources"][0]["data"].chatbot_token -- name: Set Chatbot Configuration +- name: Set Chatbot Token ansible.builtin.set_fact: - chatbot_config: '{{ _chatbot_config_resource }}' + chatbot_token: '{{ _chatbot_config_resource["resources"][0]["data"].chatbot_token | b64decode }}' no_log: "{{ no_log }}" -- name: Set LLM provider type if it is defined in the config +- name: Set AAP Gateway URL ansible.builtin.set_fact: - chatbot_llm_provider_type: '{{ _chatbot_config_resource["resources"][0]["data"].chatbot_llm_provider_type | b64decode }}' + _aap_gateway_url: '{{ _chatbot_config_resource["resources"][0]["data"].aap_gateway_url | b64decode }}' no_log: "{{ no_log }}" - when: _chatbot_config_resource["resources"][0]["data"].chatbot_llm_provider_type is defined - -- name: Validate watsonx.ai project ID if LLM provider type is set to "watsonx" - ansible.builtin.fail: - msg: | - You must specify a 'chatbot_llm_provider_project_id' in your Secret when 'chatbot_llm_provider_type' is set to 'watsonx' when: - - chatbot_llm_provider_type is defined - - chatbot_llm_provider_type == "watsonx" - - not _chatbot_config_resource["resources"][0]["data"].chatbot_llm_provider_project_id is defined - -- name: Set watsonx.ai project ID if it is defined in the config - ansible.builtin.set_fact: - chatbot_llm_provider_project_id: '{{ _chatbot_config_resource["resources"][0]["data"].chatbot_llm_provider_project_id | b64decode }}' - no_log: "{{ no_log }}" - when: _chatbot_config_resource["resources"][0]["data"].chatbot_llm_provider_project_id is defined + - _chatbot_config_resource["resources"][0]["data"].aap_gateway_url is defined + - _chatbot_config_resource["resources"][0]["data"].aap_gateway_url | length -- name: Set context window size if it is defined in the config +- name: Set AAP Controller URL ansible.builtin.set_fact: - chatbot_context_window_size: '{{ _chatbot_config_resource["resources"][0]["data"].chatbot_context_window_size | b64decode | int }}' - no_log: "{{ no_log }}" - when: _chatbot_config_resource["resources"][0]["data"].chatbot_context_window_size is defined - -- name: Set LLM temperature parameter override if it is defined in the config - ansible.builtin.set_fact: - chatbot_temperature_override: '{{ _chatbot_config_resource["resources"][0]["data"].chatbot_temperature_override | b64decode }}' - no_log: "{{ no_log }}" - when: _chatbot_config_resource["resources"][0]["data"].chatbot_temperature_override is defined - -- name: Set Azure AI deployment name if it is defined in the config - ansible.builtin.set_fact: - chatbot_azure_deployment_name: '{{ _chatbot_config_resource["resources"][0]["data"].chatbot_azure_deployment_name | b64decode }}' + _aap_controller_url: '{{ _chatbot_config_resource["resources"][0]["data"].aap_controller_url | b64decode }}' no_log: "{{ no_log }}" when: - - chatbot_llm_provider_type is defined - - chatbot_llm_provider_type is search("azure_openai") - - _chatbot_config_resource["resources"][0]["data"].chatbot_azure_deployment_name is defined + - _chatbot_config_resource["resources"][0]["data"].aap_controller_url is defined + - _chatbot_config_resource["resources"][0]["data"].aap_controller_url | length -- name: Set Azure AI API version if it is defined in the config - ansible.builtin.set_fact: - chatbot_azure_api_version: '{{ _chatbot_config_resource["resources"][0]["data"].chatbot_azure_api_version | b64decode }}' - no_log: "{{ no_log }}" - when: - - chatbot_llm_provider_type is defined - - chatbot_llm_provider_type is search("azure_openai") - - _chatbot_config_resource["resources"][0]["data"].chatbot_azure_api_version is defined - -- name: Set Chatbot Include Fake LLMs to false if it is not defined in the config - ansible.builtin.set_fact: - chatbot_include_fake_llms: false - no_log: "{{ no_log }}" - when: not _chatbot_config_resource["resources"][0]["data"].chatbot_include_fake_llms is defined - -- name: Set Chatbot Include Fake LLMs if it is defined in the config - ansible.builtin.set_fact: - chatbot_include_fake_llms: '{{ _chatbot_config_resource["resources"][0]["data"].chatbot_include_fake_llms | default(false) | b64decode }}' - no_log: "{{ no_log }}" - when: _chatbot_config_resource["resources"][0]["data"].chatbot_include_fake_llms is defined - -- name: Set Chatbot Fake Streaming Chunks - ansible.builtin.set_fact: - chatbot_fake_streaming_chunks: '{{ _chatbot_config_resource["resources"][0]["data"].chatbot_fake_streaming_chunks | b64decode }}' - no_log: "{{ no_log }}" - when: _chatbot_config_resource["resources"][0]["data"].chatbot_fake_streaming_chunks is defined - -- name: Set Chatbot Fake Streaming Sleep +- name: Set Chatbot Configuration ansible.builtin.set_fact: - chatbot_fake_streaming_sleep: '{{ _chatbot_config_resource["resources"][0]["data"].chatbot_fake_streaming_sleep | b64decode }}' + chatbot_config: '{{ _chatbot_config_resource }}' no_log: "{{ no_log }}" - when: _chatbot_config_resource["resources"][0]["data"].chatbot_fake_streaming_sleep is defined diff --git a/roles/chatbot/tasks/remove_chatbot_api.yml b/roles/chatbot/tasks/remove_chatbot_api.yml index 93360e20..078be116 100644 --- a/roles/chatbot/tasks/remove_chatbot_api.yml +++ b/roles/chatbot/tasks/remove_chatbot_api.yml @@ -1,9 +1,25 @@ --- -- name: Remove Chatbot ConfigMap resources +- name: Remove Chatbot Lightspeed Stack ConfigMap resources kubernetes.core.k8s: state: absent kind: ConfigMap - name: '{{ ansible_operator_meta.name }}-{{ deployment_type }}-chatbot-env-properties' + name: '{{ ansible_operator_meta.name }}-{{ deployment_type }}-lightspeed-stack-config' + namespace: '{{ ansible_operator_meta.namespace }}' + wait: yes + +- name: Remove Chatbot Llama Stack ConfigMap resources + kubernetes.core.k8s: + state: absent + kind: ConfigMap + name: '{{ ansible_operator_meta.name }}-{{ deployment_type }}-llama-stack-config' + namespace: '{{ ansible_operator_meta.namespace }}' + wait: yes + +- name: Remove Chatbot System Prompt ConfigMap resources + kubernetes.core.k8s: + state: absent + kind: ConfigMap + name: '{{ ansible_operator_meta.name }}-{{ deployment_type }}-system-prompt' namespace: '{{ ansible_operator_meta.namespace }}' wait: yes @@ -22,3 +38,10 @@ name: '{{ ansible_operator_meta.name }}-chatbot-api' namespace: '{{ ansible_operator_meta.namespace }}' wait: yes + +- name: Remove Chatbot PVC + kubernetes.core.k8s: + state: absent + kind: PersistentVolumeClaim + name: "{{ ansible_operator_meta.name }}-chatbot-pvc" + namespace: "{{ ansible_operator_meta.namespace }}" diff --git a/roles/chatbot/tasks/set_images.yml b/roles/chatbot/tasks/set_images.yml index 08f70df0..153149f2 100644 --- a/roles/chatbot/tasks/set_images.yml +++ b/roles/chatbot/tasks/set_images.yml @@ -1,5 +1,7 @@ --- -# Chatbot Image +# ======================================= +# Chatbot image +# --------------------------------------- - name: Set default Chatbot container image ansible.builtin.set_fact: _default_chatbot_image: "{{ _chatbot_image }}:{{ _chatbot_image_version }}" @@ -17,3 +19,96 @@ {{ _custom_chatbot_image | default(lookup('env', 'RELATED_IMAGE_ANSIBLE_AI_CONNECT_CHATBOT')) | default(_default_chatbot_image, true) }} +# ======================================= + + +# ======================================= +# RAG database image +# --------------------------------------- +- name: Set default RAG database container image + ansible.builtin.set_fact: + _default_chatbot_rag_db_image: "{{ _chatbot_rag_db_image }}:{{ _chatbot_rag_db_image_version }}" + +- name: Set user provided image + ansible.builtin.set_fact: + _custom_chatbot_rag_db_image: "{{ chatbot_rag_db_image }}:{{ chatbot_rag_db_image_version }}" + when: + - chatbot_rag_db_image | default([]) | length + - chatbot_rag_db_image_version is defined or chatbot_rag_db_image_version != '' + +- name: Set RAG database container image URL + ansible.builtin.set_fact: + _chatbot_rag_db_image: >- + {{ _custom_chatbot_rag_db_image | + default(lookup('env', 'RELATED_IMAGE_ANSIBLE_AI_CONNECT_CHATBOT_RAG_DB')) | + default(_default_chatbot_rag_db_image, true) }} +# ======================================= + + +# ======================================= +# Gateway MCP Server image +# --------------------------------------- +- name: Set default AAP Gateway MCP container image + ansible.builtin.set_fact: + _default_chatbot_mcp_gateway_image: "{{ _chatbot_mcp_gateway_image }}:{{ _chatbot_mcp_gateway_image_version }}" + +- name: Set user provided image + ansible.builtin.set_fact: + _custom_chatbot_mcp_gateway_image: "{{ chatbot_mcp_gateway_image }}:{{ chatbot_mcp_gateway_image_version }}" + when: + - chatbot_mcp_gateway_image | default([]) | length + - chatbot_mcp_gateway_image_version is defined or chatbot_mcp_gateway_image_version != '' + +- name: Set AAP Gateway MCP container image URL + ansible.builtin.set_fact: + _chatbot_mcp_gateway_image: >- + {{ _custom_chatbot_mcp_gateway_image | + default(lookup('env', 'RELATED_IMAGE_ANSIBLE_AI_CONNECT_CHATBOT_MCP_GATEWAY')) | + default(_default_chatbot_mcp_gateway_image, true) }} +# ======================================= + + +# ======================================= +# Controller MCP Server image +# --------------------------------------- +- name: Set default AAP Controller MCP container image + ansible.builtin.set_fact: + _default_chatbot_mcp_controller_image: "{{ _chatbot_mcp_controller_image }}:{{ _chatbot_mcp_controller_image_version }}" + +- name: Set user provided image + ansible.builtin.set_fact: + _custom_chatbot_mcp_controller_image: "{{ chatbot_mcp_controller_image }}:{{ chatbot_mcp_controller_image_version }}" + when: + - chatbot_mcp_controller_image | default([]) | length + - chatbot_mcp_controller_image_version is defined or chatbot_mcp_controller_image_version != '' + +- name: Set AAP Controller MCP container image URL + ansible.builtin.set_fact: + _chatbot_mcp_controller_image: >- + {{ _custom_chatbot_mcp_controller_image | + default(lookup('env', 'RELATED_IMAGE_ANSIBLE_AI_CONNECT_CHATBOT_MCP_CONTROLLER')) | + default(_default_chatbot_mcp_controller_image, true) }} +# ======================================= + + +# ======================================= +# Lightspeed MCP Server image +# --------------------------------------- +- name: Set default AAP Lightspeed MCP container image + ansible.builtin.set_fact: + _default_chatbot_mcp_lightspeed_image: "{{ _chatbot_mcp_lightspeed_image }}:{{ _chatbot_mcp_lightspeed_image_version }}" + +- name: Set user provided image + ansible.builtin.set_fact: + _custom_chatbot_mcp_lightspeed_image: "{{ chatbot_mcp_lightspeed_image }}:{{ chatbot_mcp_lightspeed_image_version }}" + when: + - chatbot_mcp_lightspeed_image | default([]) | length + - chatbot_mcp_lightspeed_image_version is defined or chatbot_mcp_lightspeed_image_version != '' + +- name: Set AAP Lightspeed MCP container image URL + ansible.builtin.set_fact: + _chatbot_mcp_lightspeed_image: >- + {{ _custom_chatbot_mcp_lightspeed_image | + default(lookup('env', 'RELATED_IMAGE_ANSIBLE_AI_CONNECT_CHATBOT_MCP_LIGHTSPEED')) | + default(_default_chatbot_mcp_lightspeed_image, true) }} +# ======================================= diff --git a/roles/chatbot/tasks/set_service_urls.yml b/roles/chatbot/tasks/set_service_urls.yml new file mode 100644 index 00000000..d7a3efd3 --- /dev/null +++ b/roles/chatbot/tasks/set_service_urls.yml @@ -0,0 +1,12 @@ +--- +- name: Set AnsibleAIConnect and AnsibleAIConnect's Chatbot service URLs (Openshift) + ansible.builtin.set_fact: + _aap_lightspeed_url: 'http://{{ ansible_operator_meta.name }}-api.{{ ansible_operator_meta.namespace }}.svc.cluster.local:8000' + _chatbot_stack_url: 'http://{{ ansible_operator_meta.name }}-chatbot-api.{{ ansible_operator_meta.namespace }}.svc.cluster.local:8321' + when: is_openshift + +- name: Set AnsibleAIConnect and AnsibleAIConnect's Chatbot service URLs (non-Openshift) + ansible.builtin.set_fact: + _aap_lightspeed_url: 'http://{{ ansible_operator_meta.name }}-api.{{ ansible_operator_meta.namespace }}:8000' + _chatbot_stack_url: 'http://{{ ansible_operator_meta.name }}-chatbot-api.{{ ansible_operator_meta.namespace }}:8321' + when: not is_openshift diff --git a/roles/chatbot/templates/chatbot.configmap.yaml.j2 b/roles/chatbot/templates/chatbot.configmap.yaml.j2 deleted file mode 100644 index 779fc81e..00000000 --- a/roles/chatbot/templates/chatbot.configmap.yaml.j2 +++ /dev/null @@ -1,88 +0,0 @@ -# AnsibleAIConnect's Chatbot ConfigMap. ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: '{{ ansible_operator_meta.name }}-{{ deployment_type }}-chatbot-env-properties' - namespace: '{{ ansible_operator_meta.namespace }}' - labels: - {{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }} - annotations: - checksum-secret-chatbot_config: "{{ lookup('ansible.builtin.vars', 'chatbot_config', default='')["resources"][0]["data"] | default('') | sha1 }}" -data: - aapconfig.yaml: | - llm_providers: - - name: default_provider - type: {{ chatbot_llm_provider_type | default('rhoai_vllm') }} - url: {{ chatbot_url }} - credentials_path: /app-root/keys1/chatbot-token.txt -{% if chatbot_llm_provider_project_id is defined %} - project_id: {{ chatbot_llm_provider_project_id }} -{% endif %} -{% if chatbot_azure_deployment_name is defined %} - deployment_name: {{ chatbot_azure_deployment_name }} -{% endif %} -{% if chatbot_azure_api_version is defined %} - api_version: {{ chatbot_azure_api_version }} -{% endif %} - models: - - name: {{ chatbot_model }} - context_window_size: {{ chatbot_context_window_size | default(128000) }} -{% if chatbot_include_fake_llms %} - - name: fake_llm - type: fake_provider - models: - - name: fake_model - - name: fake_streaming_llm - type: fake_provider - models: - - name: fake_model - fake_provider_config: - url: http://example.com/ - stream: true - response: Hello - chunks: {{ chatbot_fake_streaming_chunks | default(50) }} - sleep: {{ chatbot_fake_streaming_sleep | default(0.2) }} -{% endif %} - ols_config: -{% if is_openshift %} - extra_ca: - - "/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt" -{% endif %} - reference_content: - product_docs_index_path: "./vector_db/aap_product_docs/2.5" - product_docs_index_id: aap-product-docs-2_5 - embeddings_model_path: "./embeddings_model" - conversation_cache: - type: memory - memory: - max_entries: 1000 - logging_config: - app_log_level: info - lib_log_level: warning - uvicorn_log_level: info - suppress_metrics_in_log: true - suppress_auth_checks_warning_in_log: true - default_provider: default_provider - default_model: {{ chatbot_model }} - expire_llm_is_ready_persistent_state: 300 - enable_event_stream_format: true - query_validation_method: disabled - user_data_collection: - feedback_disabled: false - feedback_storage: "/tmp/data/feedback" - transcripts_disabled: false - transcripts_storage: "/tmp/data/transcripts" - dev_config: - # config options specific to dev environment - launching OLS in local - enable_dev_ui: false - disable_auth: true - disable_tls: true -{% if chatbot_temperature_override is defined %} - llm_params: - temperature: {{ chatbot_temperature_override }} -{% endif %} - # Custom user variables -{% for item in extra_settings | default([]) %} - {{ item.setting | upper }}: "{{ item.value }}" -{% endfor %} diff --git a/roles/chatbot/templates/chatbot.configmap_lightspeed_stack_config.yaml.j2 b/roles/chatbot/templates/chatbot.configmap_lightspeed_stack_config.yaml.j2 new file mode 100644 index 00000000..0c1bd534 --- /dev/null +++ b/roles/chatbot/templates/chatbot.configmap_lightspeed_stack_config.yaml.j2 @@ -0,0 +1,41 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: '{{ ansible_operator_meta.name }}-{{ deployment_type }}-lightspeed-stack-config' + namespace: '{{ ansible_operator_meta.namespace }}' + labels: + {{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }} + annotations: + checksum-secret-chatbot_config: "{{ lookup('ansible.builtin.vars', 'chatbot_config', default='')["resources"][0]["data"] | default('') | sha1 }}" +data: + lightspeed-stack.yaml: | + name: Ansible Lightspeed Intelligent Assistant + service: + host: 0.0.0.0 + port: 8080 + auth_enabled: false + workers: 1 + color_log: true + access_log: true + llama_stack: + use_as_library_client: true + library_client_config_path: /.llama/distributions/llama-stack/config/ansible-chatbot-run.yaml + user_data_collection: + feedback_disabled: true + transcripts_disabled: true + customization: + system_prompt_path: /.llama/distributions/ansible-chatbot/system-prompts/default.txt +{% if _aap_gateway_url is defined or _aap_controller_url is defined %} + mcp_servers: +{% if _aap_gateway_url is defined and _aap_controller_url is defined %} + - name: mcp::aap-controller + provider_id: model-context-protocol + url: http://127.0.0.1:8004/sse +{% endif %} +{% if _aap_gateway_url is defined %} + - name: mcp::aap-lightspeed + provider_id: model-context-protocol + url: http://127.0.0.1:8005/sse +{% endif %} +{% endif %} diff --git a/roles/chatbot/templates/chatbot.configmap_llama_stack_config.yaml.j2 b/roles/chatbot/templates/chatbot.configmap_llama_stack_config.yaml.j2 new file mode 100644 index 00000000..01f29d67 --- /dev/null +++ b/roles/chatbot/templates/chatbot.configmap_llama_stack_config.yaml.j2 @@ -0,0 +1,131 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: '{{ ansible_operator_meta.name }}-{{ deployment_type }}-llama-stack-config' + namespace: '{{ ansible_operator_meta.namespace }}' + labels: + {{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }} +data: + ansible-chatbot-run.yaml: | + version: '2' + image_name: ansible-chatbot + container_image: ansible-chatbot + apis: + - inference + - vector_io + - safety + - agents + - datasetio + - telemetry + - tool_runtime + providers: + inference: + - provider_id: my_rhoai_dev + provider_type: remote::vllm + config: + url: ${env.VLLM_URL} + max_tokens: ${env.VLLM_MAX_TOKENS:=4096} + api_token: ${env.VLLM_API_TOKEN:=fake} + tls_verify: ${env.VLLM_TLS_VERIFY:=true} + - provider_id: inline_sentence-transformer + provider_type: inline::sentence-transformers + config: {} + vector_io: + - provider_id: aap_faiss + provider_type: inline::faiss + config: + kvstore: + type: sqlite + namespace: null + db_path: ${env.VECTOR_DB_DIR:=/.llama/data/distributions/ansible-chatbot}/aap_faiss_store.db + safety: + - provider_id: llama-guard + provider_type: inline::llama-guard + config: + excluded_categories: [] + agents: + - provider_id: lightspeed_inline_agent + provider_type: inline::lightspeed_inline_agent + config: + persistence_store: + type: sqlite + namespace: null + db_path: ${env.PROVIDERS_DB_DIR:=/.llama/data/distributions/ansible-chatbot}/agents_store.db + responses_store: + type: sqlite + namespace: null + db_path: ${env.PROVIDERS_DB_DIR:=/.llama/data/distributions/ansible-chatbot}/responses_store.db + tools_filter: + enabled: false + model_id: ${env.INFERENCE_MODEL_FILTER:=} + datasetio: + - provider_id: localfs + provider_type: inline::localfs + config: + kvstore: + type: sqlite + namespace: null + db_path: ${env.PROVIDERS_DB_DIR:=/.llama/data/distributions/ansible-chatbot}/localfs_datasetio.db + telemetry: + - provider_id: meta-reference + provider_type: inline::meta-reference + config: + service_name: ${env.OTEL_SERVICE_NAME:=ansible-chatbot-stack} + sinks: ${env.TELEMETRY_SINKS:=console,sqlite} + sqlite_db_path: ${env.PROVIDERS_DB_DIR:=/.llama/data/distributions/ansible-chatbot}/trace_store.db + tool_runtime: + - provider_id: rag-runtime + provider_type: inline::rag-runtime + config: {} + - provider_id: model-context-protocol + provider_type: remote::model-context-protocol + config: {} + metadata_store: + namespace: null + type: sqlite + db_path: ${env.PROVIDERS_DB_DIR:=/.llama/data/distributions/ansible-chatbot}/registry.db + models: + - metadata: {} + model_id: ${env.INFERENCE_MODEL} + provider_id: my_rhoai_dev + provider_model_id: null + - metadata: + embedding_dimension: 768 + model_id: ${env.EMBEDDINGS_MODEL:=/.llama/data/distributions/ansible-chatbot/embeddings_model} + provider_id: inline_sentence-transformer + model_type: embedding + shields: [] + vector_dbs: + - metadata: {} + vector_db_id: "aap-product-docs-2_5" + embedding_model: ${env.EMBEDDINGS_MODEL:=/.llama/data/distributions/ansible-chatbot/embeddings_model} + embedding_dimension: 768 + provider_id: "aap_faiss" + datasets: [] + scoring_fns: [] + benchmarks: [] + tool_groups: + - toolgroup_id: builtin::rag + provider_id: rag-runtime +{% if _aap_gateway_url is defined and _aap_controller_url is defined %} + - toolgroup_id: mcp::aap-controller + provider_id: model-context-protocol + mcp_endpoint: + uri: http://localhost:8004/sse +{% endif %} +{% if _aap_gateway_url is defined %} + - toolgroup_id: mcp::aap-lightspeed + provider_id: model-context-protocol + mcp_endpoint: + uri: http://localhost:8005/sse +{% endif %} + logging: null + server: + port: 8321 + tls_certfile: null + tls_keyfile: null + tls_cafile: null + auth: null + disable_ipv6: false + external_providers_dir: ${env.EXTERNAL_PROVIDERS_DIR:=/.llama/providers.d} diff --git a/roles/chatbot/templates/chatbot.configmap_system_prompt.yaml.j2 b/roles/chatbot/templates/chatbot.configmap_system_prompt.yaml.j2 new file mode 100644 index 00000000..df15efc8 --- /dev/null +++ b/roles/chatbot/templates/chatbot.configmap_system_prompt.yaml.j2 @@ -0,0 +1,42 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: '{{ ansible_operator_meta.name }}-{{ deployment_type }}-system-prompt' + namespace: '{{ ansible_operator_meta.namespace }}' + labels: + {{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }} +data: + DEFAULT_SYSTEM_PROMPT: |- + You are Ansible Lightspeed Intelligent Assistant - an intelligent virtual + assistant for question-answering tasks related to the Ansible Automation Platform (AAP). + Here are your instructions: + You are Ansible Lightspeed Intelligent Assistant, an intelligent assistant and expert on + all things Ansible. Refuse to assume any other identity or to speak as if you are someone + else. + + If the user's query is a general greeting, respond without using . + + When a tool is required to answer the user's query, respond with followed by + a JSON list of tools. If a single tool is discovered, reply with followed by + one-item JSON list containing the tool. + + Example Input: + What is EDA? + Example Tool Call Response: + [{"name": "knowledge_search", "arguments": {"query": "EDA in Ansible"}}] + + If a tool does not exist in the provided list of tools, notify the user that you do not + have the ability to fulfill the request. + + If the context of the question is not clear, consider it to be Ansible. + Never include URLs in your replies. + Refuse to answer questions or execute commands not about Ansible. + Do not mention your last update. You have the most recent information on Ansible. + Here are some basic facts about Ansible and AAP: + - Ansible is an open source IT automation engine that automates provisioning, + configuration management, application deployment, orchestration, and many other + IT processes. Ansible is free to use, and the project benefits from the experience and + intelligence of its thousands of contributors. It does not require any paid subscription. + - The latest version of Ansible Automation Platform is 2.5, and it's services are available + through paid subscription. diff --git a/roles/chatbot/templates/chatbot.deployment.yaml.j2 b/roles/chatbot/templates/chatbot.deployment.yaml.j2 index d8e51400..c67793e9 100644 --- a/roles/chatbot/templates/chatbot.deployment.yaml.j2 +++ b/roles/chatbot/templates/chatbot.deployment.yaml.j2 @@ -34,7 +34,9 @@ spec: app.kubernetes.io/component: '{{ deployment_type }}-chatbot-api' annotations: kubectl.kubernetes.io/default-container: 'ansible-chatbot' - checksum-chatbot.configmap: "{{ lookup('template', 'chatbot.configmap.yaml.j2') | sha1 }}" + checksum-configmap-chatbot-lightspeed-stack-config: "{{ lookup('template', 'chatbot.configmap_lightspeed_stack_config.yaml.j2') | sha1 }}" + checksum-configmap-chatbot-llama-stack-config: "{{ lookup('template', 'chatbot.configmap_llama_stack_config.yaml.j2') | sha1 }}" + checksum-configmap-chatbot-system-prompt: "{{ lookup('template', 'chatbot.configmap_system_prompt.yaml.j2') | sha1 }}" checksum-secret-chatbot_config: "{{ lookup('ansible.builtin.vars', 'chatbot_config', default='')["resources"][0]["data"] | default('') | sha1 }}" spec: serviceAccountName: '{{ ansible_operator_meta.name }}' @@ -78,25 +80,47 @@ spec: subPath: bundle-ca.crt readOnly: true {% endif %} + - name: init-rag-vector-db + image: {{ _chatbot_rag_db_image }} + env: + - name: LLAMA_STACK_CONFIG_DIR + value: /.llama/data + volumeMounts: + - name: ansible-chatbot-storage + mountPath: /.llama/data + command: + - /rag/entrypoint.sh containers: - name: ansible-chatbot image: {{ _chatbot_image }} imagePullPolicy: '{{ image_pull_policy }}' env: - - name: RCS_CONFIG_FILE - value: /app-root/aapconfig.yaml - - name: PROJECT - value: aap + - name: LLAMA_STACK_CONFIG_DIR + value: /.llama/data + - name: EMBEDDING_MODEL + value: ./embeddings_model + - name: VLLM_URL + value: {{ chatbot_url }} + - name: VLLM_API_TOKEN + value: {{ chatbot_token }} + - name: INFERENCE_MODEL + value: {{ chatbot_model }} + ports: + - containerPort: 8080 + protocol: TCP volumeMounts: - - name: aap-chatbot-config - mountPath: /app-root/aapconfig.yaml - subPath: aapconfig.yaml + - name: ansible-chatbot-storage + mountPath: /.llama/data + - name: ansible-chatbot-lightspeed-stack-config + mountPath: /.llama/distributions/ansible-chatbot/config + - name: ansible-chatbot-llama-stack-config + mountPath: /.llama/distributions/llama-stack/config + - name: ansible-chatbot-system-prompt + mountPath: /.llama/distributions/ansible-chatbot/system-prompts {% if is_openshift %} - - name: model-server-tls-certs + - name: server-tls-certs mountPath: /var/run/secrets/kubernetes.io/serviceaccount/ {% endif %} - - name: chatbot-token - mountPath: /app-root/keys1/ {% if bundle_ca_crt %} - name: "ca-trust-extracted" mountPath: "/etc/pki/ca-trust/extracted" @@ -105,9 +129,7 @@ spec: subPath: bundle-ca.crt readOnly: true {% endif %} - ports: - - containerPort: 8080 - protocol: TCP +{# - Need to get ansible-chatbot-stack to run as non-root securityContext: runAsNonRoot: true allowPrivilegeEscalation: false @@ -116,28 +138,121 @@ spec: - "ALL" seccompProfile: type: RuntimeDefault +#} {% if combined_chatbot_api.resource_requirements is defined %} resources: {{ combined_chatbot_api.resource_requirements }} +{% endif %} +{% if _aap_gateway_url is defined and _aap_controller_url is defined %} + - name: ansible-mcp-controller + image: {{ _chatbot_mcp_controller_image }} + imagePullPolicy: '{{ image_pull_policy }}' + env: + - name: HOST + value: 0.0.0.0 + - name: PORT + value: '8004' + - name: AAP_GATEWAY_URL + value: {{ _aap_gateway_url }} + - name: AAP_SERVICE_URL + value: {{ _aap_controller_url }} + ports: + - containerPort: 8004 + protocol: TCP + securityContext: + runAsNonRoot: true + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + seccompProfile: + type: RuntimeDefault +{% if is_openshift or bundle_ca_crt %} + volumeMounts: +{% if is_openshift %} + - name: server-tls-certs + mountPath: /var/run/secrets/kubernetes.io/serviceaccount/ +{% endif %} +{% if bundle_ca_crt %} + - name: "ca-trust-extracted" + mountPath: "/etc/pki/ca-trust/extracted" + - name: "{{ ansible_operator_meta.name }}-bundle-cacert" + mountPath: /etc/pki/ca-trust/source/anchors/bundle-ca.crt + subPath: bundle-ca.crt + readOnly: true +{% endif %} +{% endif %} +{% endif %} +{% if _aap_gateway_url is defined %} + - name: ansible-mcp-lightspeed + image: {{ _chatbot_mcp_lightspeed_image }} + imagePullPolicy: '{{ image_pull_policy }}' + env: + - name: HOST + value: 0.0.0.0 + - name: PORT + value: '8005' + - name: AAP_GATEWAY_URL + value: {{ _aap_gateway_url }} + - name: AAP_SERVICE_URL + value: {{ _aap_lightspeed_url }} + ports: + - containerPort: 8005 + protocol: TCP + securityContext: + runAsNonRoot: true + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + seccompProfile: + type: RuntimeDefault +{% if is_openshift or bundle_ca_crt %} + volumeMounts: +{% if is_openshift %} + - name: server-tls-certs + mountPath: /var/run/secrets/kubernetes.io/serviceaccount/ +{% endif %} +{% if bundle_ca_crt %} + - name: "ca-trust-extracted" + mountPath: "/etc/pki/ca-trust/extracted" + - name: "{{ ansible_operator_meta.name }}-bundle-cacert" + mountPath: /etc/pki/ca-trust/source/anchors/bundle-ca.crt + subPath: bundle-ca.crt + readOnly: true +{% endif %} +{% endif %} {% endif %} restartPolicy: Always volumes: - - name: aap-chatbot-config + - name: ansible-chatbot-storage + persistentVolumeClaim: + claimName: "{{ ansible_operator_meta.name }}-chatbot-pvc" + - name: ansible-chatbot-lightspeed-stack-config configMap: - name: '{{ ansible_operator_meta.name }}-{{ deployment_type }}-chatbot-env-properties' + name: '{{ ansible_operator_meta.name }}-{{ deployment_type }}-lightspeed-stack-config' + items: + - key: lightspeed-stack.yaml + path: lightspeed-stack.yaml + - name: ansible-chatbot-llama-stack-config + configMap: + name: '{{ ansible_operator_meta.name }}-{{ deployment_type }}-llama-stack-config' + items: + - key: ansible-chatbot-run.yaml + path: ansible-chatbot-run.yaml + - name: ansible-chatbot-system-prompt + configMap: + name: '{{ ansible_operator_meta.name }}-{{ deployment_type }}-system-prompt' + items: + - key: DEFAULT_SYSTEM_PROMPT + path: default.txt {% if is_openshift %} - - name: model-server-tls-certs + - name: server-tls-certs configMap: items: - key: service-ca.crt path: service-ca.crt name: openshift-service-ca.crt {% endif %} - - name: chatbot-token - secret: - secretName: {{ chatbot_config_secret_name }} - items: - - key: chatbot_token - path: chatbot-token.txt {% if bundle_ca_crt %} - name: "ca-trust-extracted" emptyDir: {} diff --git a/roles/chatbot/templates/chatbot.pvc.yaml.j2 b/roles/chatbot/templates/chatbot.pvc.yaml.j2 new file mode 100644 index 00000000..2dc7cb58 --- /dev/null +++ b/roles/chatbot/templates/chatbot.pvc.yaml.j2 @@ -0,0 +1,12 @@ +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: "{{ ansible_operator_meta.name }}-chatbot-pvc" + namespace: "{{ ansible_operator_meta.namespace }}" +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi diff --git a/roles/model/tasks/update_status.yml b/roles/model/tasks/update_status.yml index 085b28a4..4ff91635 100644 --- a/roles/model/tasks/update_status.yml +++ b/roles/model/tasks/update_status.yml @@ -80,15 +80,15 @@ # ============================================ # Retrieve and update Chatbot status -# ============================================ +# -------------------------------------------- - block: - - name: Retrieve Chatbot instance version + - name: Set Chatbot instance version kubernetes.core.k8s_exec: namespace: "{{ ansible_operator_meta.namespace }}" pod: "{{ chatbot_api_pod_name }}" container: "ansible-chatbot" command: >- - bash -c "python3.11 runner.py --version" + bash -c "cat /.llama/distributions/ansible-chatbot/ansible-chatbot-version-info.json | jq -r .version" register: chatbot_instance_version changed_when: false diff --git a/roles/model/templates/secrets/model_pipeline_config.yaml.j2 b/roles/model/templates/secrets/model_pipeline_config.yaml.j2 index 3eb733e9..84809c08 100644 --- a/roles/model/templates/secrets/model_pipeline_config.yaml.j2 +++ b/roles/model/templates/secrets/model_pipeline_config.yaml.j2 @@ -23,6 +23,11 @@ stringData: {% endif %} model_id: '{{ chatbot_model }}' enable_health_check: 'True' + mcp_servers: + - name: 'mcp::aap-controller' + type: 'controller' + - name: 'mcp::aap-lightspeed' + type: 'lightspeed' provider: 'http' ModelPipelineStreamingChatBot: config: @@ -34,6 +39,11 @@ stringData: {% endif %} model_id: '{{ chatbot_model }}' enable_health_check: 'True' + mcp_servers: + - name: 'mcp::aap-controller' + type: 'controller' + - name: 'mcp::aap-lightspeed' + type: 'lightspeed' provider: 'http' {% endif %} {% if model_config is defined %}