\ No newline at end of file
diff --git a/app/_includes/checkbox.html b/app/_includes/checkbox.html
index 1dbe68f8a6..581a819580 100644
--- a/app/_includes/checkbox.html
+++ b/app/_includes/checkbox.html
@@ -1,6 +1,9 @@
\ No newline at end of file
diff --git a/app/_layouts/prompts/overview.html b/app/_layouts/prompts/overview.html
new file mode 100644
index 0000000000..e0e24a50c6
--- /dev/null
+++ b/app/_layouts/prompts/overview.html
@@ -0,0 +1,5 @@
+---
+layout: without_aside
+---
+
+{{content}}
diff --git a/app/_plugins/converters/syntax_highlight.rb b/app/_plugins/converters/syntax_highlight.rb
index e5d63646ca..db66015168 100644
--- a/app/_plugins/converters/syntax_highlight.rb
+++ b/app/_plugins/converters/syntax_highlight.rb
@@ -17,13 +17,15 @@ def render_shiki(el, _indent) # rubocop:disable Metrics/AbcSize,Metrics/MethodLe
id = SecureRandom.uuid
snippet = CodeHighlighter.new.highlight(code, language, id)
+
Liquid::Template.parse(template, { line_numbers: true }).render(
{
'codeblock' => {
'copy' => copy,
'css_classes' => el.attr['class'],
'collapsible' => el.attr.fetch('class', '').include?('collapsible'),
- 'render_header' => !data['data-file'].nil?,
+ 'ask_kai' => ask_kai(data, code),
+ 'render_header' => !data['data-file'].nil? || !ask_kai(data, code).nil?,
'id' => id,
'data' => data,
'snippet' => snippet
@@ -54,6 +56,12 @@ def data_attributes(attr)
data[key] = value if key.start_with?('data-')
end
end
+
+ def ask_kai(data, code)
+ return nil if data['data-ask-kai'].nil?
+
+ "https://cloud.konghq.com/?agent=true&agent-prompt=#{URI.encode_www_form_component(code)}"
+ end
end
end
end
diff --git a/app/_plugins/generators/data/search_tags/base.rb b/app/_plugins/generators/data/search_tags/base.rb
index 96d1c628fc..7fd6578fb7 100644
--- a/app/_plugins/generators/data/search_tags/base.rb
+++ b/app/_plugins/generators/data/search_tags/base.rb
@@ -13,7 +13,8 @@ class Base # rubocop:disable Style/Documentation
'plugin_example' => 'PluginExample',
'reference' => 'Reference',
'policy' => 'Policy',
- 'support' => 'Support'
+ 'support' => 'Support',
+ 'prompt' => 'Prompt'
}.freeze
def self.make_for(site:, page:)
diff --git a/app/_plugins/generators/data/search_tags/prompt.rb b/app/_plugins/generators/data/search_tags/prompt.rb
new file mode 100644
index 0000000000..528af14b8e
--- /dev/null
+++ b/app/_plugins/generators/data/search_tags/prompt.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+require_relative 'base'
+
+module Jekyll
+ module Data
+ module SearchTags
+ class Prompt < Base
+ end
+ end
+ end
+end
diff --git a/app/_plugins/generators/data/title/base.rb b/app/_plugins/generators/data/title/base.rb
index 7c7a8dd93e..f63b845d06 100644
--- a/app/_plugins/generators/data/title/base.rb
+++ b/app/_plugins/generators/data/title/base.rb
@@ -9,6 +9,8 @@ def self.make_for(page:, site:) # rubocop:disable Metrics/AbcSize,Metrics/Cyclom
APIPage.new(page:, site:)
elsif page.url.start_with?('/plugins/')
Plugin.new(page:, site:)
+ elsif page.url.start_with?('/prompts/')
+ Prompt.new(page:, site:)
elsif page.url.start_with?('/mesh/policies/') || page.url.start_with?('/event-gateway/policies/')
Policy.new(page:, site:)
elsif page.data['content_type'] && page.data['content_type'] == 'reference'
diff --git a/app/_plugins/generators/data/title/prompt.rb b/app/_plugins/generators/data/title/prompt.rb
new file mode 100644
index 0000000000..14636b5e4b
--- /dev/null
+++ b/app/_plugins/generators/data/title/prompt.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require_relative 'base'
+
+module Jekyll
+ module Data
+ module Title
+ class Prompt < Base
+ def title_sections
+ [page_title, 'Prompts']
+ end
+
+ def llm_title
+ page_title
+ end
+ end
+ end
+ end
+end
diff --git a/app/_plugins/generators/prompts.rb b/app/_plugins/generators/prompts.rb
new file mode 100644
index 0000000000..52a9b19487
--- /dev/null
+++ b/app/_plugins/generators/prompts.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Jekyll
+ class PromptsGenerator < Jekyll::Generator
+ priority :high
+
+ def generate(site)
+ site.data['prompts'] ||= []
+ Jekyll::PromptPages::Generator.run(site)
+ end
+ end
+end
diff --git a/app/_plugins/generators/prompts/generator.rb b/app/_plugins/generators/prompts/generator.rb
new file mode 100644
index 0000000000..42b19da5e4
--- /dev/null
+++ b/app/_plugins/generators/prompts/generator.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Jekyll
+ module PromptPages
+ class Generator
+ PROMPTS_FOLDER = '_prompts'
+
+ def self.run(site)
+ new(site).run
+ end
+
+ attr_reader :site
+
+ def initialize(site)
+ @site = site
+ end
+
+ def run
+ return if site.config.dig('skip', 'prompts')
+
+ Dir.glob(File.join(site.source, "#{PROMPTS_FOLDER}/*.{yaml,yml}")).each do |file|
+ slug = File.basename(file, File.extname(file))
+ prompt = Jekyll::PromptPages::Prompt.new(file:, slug:)
+
+ generate_overview_page(prompt)
+ end
+ end
+
+ private
+
+ def generate_overview_page(prompt)
+ overview = Jekyll::PromptPages::Pages::Overview
+ .new(prompt:)
+ .to_jekyll_page
+
+ site.data['prompts'] << overview
+ site.pages << overview
+ end
+ end
+ end
+end
diff --git a/app/_plugins/generators/prompts/pages/base.rb b/app/_plugins/generators/prompts/pages/base.rb
new file mode 100644
index 0000000000..43203d87df
--- /dev/null
+++ b/app/_plugins/generators/prompts/pages/base.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+require_relative '../../../lib/site_accessor'
+
+module Jekyll
+ module PromptPages
+ module Pages
+ class Base
+ include Jekyll::SiteAccessor
+
+ attr_reader :prompt
+
+ def initialize(prompt:)
+ @prompt = prompt
+ end
+
+ def to_jekyll_page
+ CustomJekyllPage.new(site:, page: self)
+ end
+
+ def dir
+ url
+ end
+
+ def relative_path
+ @relative_path ||= prompt.file.gsub("#{site.source}/", '')
+ end
+
+ def data
+ {
+ 'title' => prompt.title,
+ 'description' => prompt.description,
+ 'extended_description' => prompt.extended_description,
+ 'products' => prompt.products,
+ 'prompts' => prompt.prompts,
+ 'slug' => prompt.slug,
+ 'content_type' => 'prompt',
+ 'layout' => layout,
+ 'breadcrumbs' => ['/prompts/']
+ }
+ end
+
+ def url
+ @url ||= self.class.url(prompt)
+ end
+ end
+ end
+ end
+end
diff --git a/app/_plugins/generators/prompts/pages/overview.rb b/app/_plugins/generators/prompts/pages/overview.rb
new file mode 100644
index 0000000000..993a675708
--- /dev/null
+++ b/app/_plugins/generators/prompts/pages/overview.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Jekyll
+ module PromptPages
+ module Pages
+ class Overview < Base
+ def self.url(prompt)
+ "/prompts/#{prompt.slug}/"
+ end
+
+ def content
+ @content ||= File.read('app/_includes/prompts/overview.md')
+ end
+
+ def layout
+ 'prompts/overview'
+ end
+
+ def data
+ super.merge('overview?' => true, 'content_type' => 'prompt')
+ end
+ end
+ end
+ end
+end
diff --git a/app/_plugins/generators/prompts/prompt.rb b/app/_plugins/generators/prompts/prompt.rb
new file mode 100644
index 0000000000..4fa523b9dd
--- /dev/null
+++ b/app/_plugins/generators/prompts/prompt.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'yaml'
+require_relative '../../lib/site_accessor'
+
+module Jekyll
+ module PromptPages
+ class Prompt
+ include Jekyll::SiteAccessor
+
+ attr_reader :file, :slug
+
+ def initialize(file:, slug:)
+ @file = file
+ @slug = slug
+ end
+
+ def metadata
+ @metadata ||= YAML.load_file(@file)
+ end
+
+ def title
+ @title ||= metadata.fetch('title')
+ end
+
+ def description
+ @description ||= metadata.fetch('description')
+ end
+
+ def extended_description
+ @extended_description ||= metadata.fetch('extended_description', description)
+ end
+
+ def products
+ @products ||= metadata.fetch('products', [])
+ end
+
+ def prompts
+ @prompts ||= metadata.fetch('prompts', [])
+ end
+ end
+ end
+end
diff --git a/app/_prompts/debugging.yaml b/app/_prompts/debugging.yaml
new file mode 100644
index 0000000000..18a0a1d70b
--- /dev/null
+++ b/app/_prompts/debugging.yaml
@@ -0,0 +1,11 @@
+title: Debugging with KAi
+description: Help diagnose and fix issues in your Kong configuration with KAi's debugging capabilities.
+extended_description: Help diagnose and fix issues in your Kong configuration with KAi's debugging capabilities. Learn how to use KAi to identify and resolve common problems effectively.
+products:
+ - gateway
+
+prompts:
+ - Why is my `/api/checkout` endpoint slow? Create a debugging session, focusing on high-latency requests, and summarize where time is being spent.
+ - Help me initiate a debugging session to troubleshoot the recent latency spikes and give me an analysis of the results with recommendations.
+ - Help me diagnose intermittent 502s on my [ROUTE_NAME] Route.
+ - I'm seeing 429 errors on my API Gateway route. Walk me through diagnosing a rate limiting issue, including how to check plugin config, consumer credentials, and request headers.
diff --git a/app/_prompts/security.yaml b/app/_prompts/security.yaml
new file mode 100644
index 0000000000..facf19c5d8
--- /dev/null
+++ b/app/_prompts/security.yaml
@@ -0,0 +1,12 @@
+
+title: Security with KAi
+description: Enhance the security of your Kong configuration with KAi's security auditing capabilities.
+extended_description: Enhance the security of your Kong configuration with KAi's security auditing capabilities. Learn how to use KAi to identify and fix common security misconfigurations effectively.
+products:
+ - gateway
+ - event-gateway
+
+prompts:
+ - "Audit my [CONTROL_PLANE] control plane for common security misconfigurations (for example: auth missing, weak TLS, or overly broad CORS). Suggest prioritized fixes."
+ - "Suggest a safe baseline policy set for a public API (rate limiting, authentication, and logging) and show how to apply it to the [SERVICE_NAME] Gateway Service."
+ - "Check if any Routes in my [CONTROL_PLANE] control plane expose endpoints without authentication, and list them."
diff --git a/app/prompts.html b/app/prompts.html
new file mode 100644
index 0000000000..674aebcb22
--- /dev/null
+++ b/app/prompts.html
@@ -0,0 +1,89 @@
+---
+title: Prompt Hub
+layout: default
+hub: true
+no_edit_link: true
+edit_and_issue_links: false
+description: A curated collection of AI prompts for use with KAi, Konnect's AI assistant.
+---
+{% if page.output_format == 'markdown'%}
+{%- for category in site.data.prompts %}
+- [{{category.title | liquify }}]({{ category.url }}): {{ category.description | liquify }}{% for prompt in category.prompts %}
+ - {{ prompt | liquify }}{%- endfor -%}{%- endfor %}
+{% else %}
+
+
+
+
+
Prompt Hub
{% include components/llm_dropdown.html url=page.url %}
+ A curated collection of AI prompts for KAi, Konnect's built-in AI assistant.
+ Browse, filter, and run prompts directly in your Konnect environment.
+
+ {% for prompt in site.data.prompts %}
+ {% include cards/prompt.html prompt=prompt %}
+ {% endfor %}
+
+
+
+
+
+
+{% endif %}
\ No newline at end of file
diff --git a/jekyll-dev.yml b/jekyll-dev.yml
index 173923f61c..21ad5ff278 100644
--- a/jekyll-dev.yml
+++ b/jekyll-dev.yml
@@ -12,6 +12,7 @@ skip:
auto_generated: true # skip auto_generated references, i.e. app/_referneces
mesh: true # skip kuma to mesh generation
llm_pages: true # skip markdown pages generation
+ prompts: true
# exclude app/_references
# even though we set skip.auto_generated: true and the collection has output:false