Skip to content

Conversation

@rexa0310
Copy link
Contributor

@rexa0310 rexa0310 commented Nov 4, 2025

Summary by CodeRabbit

  • New Features

    • Added crud as a context source.
    • Introduced explicit system_prompt and user_prompt fields.
    • Added similarity_config per context for similarity controls.
  • Refactor

    • Replaced per-prompt blocks with a multi-context prompt model.
    • Updated validations, public models, APIs, and tests to use contexts instead of the prior prompt structure.

@coderabbitai
Copy link

coderabbitai bot commented Nov 4, 2025

Walkthrough

This PR replaces per-prompt llm_prompts with a contexts list and adds explicit system_prompt and user_prompt fields. LlmPromptContext, hyperparameterssimilarity_config, validation and processors updated to the new shape, and crud prompt source is supported. Prompt assembly now iterates contexts and includes user_prompt in LLM payload.

Changes

Cohort / File(s) Summary
Core data & public models
kairon/shared/actions/data_objects.py, kairon/shared/data/data_models.py
Renamed LlmPromptContext; removed hyperparameters/instructions; added similarity_config; replaced llm_prompts with contexts and added system_prompt and user_prompt fields on PromptAction and public models.
Validation / Utilities
kairon/importer/validator/file_validator.py, kairon/shared/data/data_validation.py, kairon/shared/utils.py
Updated validation signatures to accept contexts, validate similarity_config (top_results/similarity_threshold), allowed sources include crud, and adjusted system/history prompt checks and error messages.
Runtime processors & exports
kairon/shared/data/processor.py, kairon/shared/cognition/processor.py, kairon/shared/llm/processor.py
Replaced llm_prompts usages with contexts; updated DB queries to contexts__*, changed exported symbols to include Context, and simplified similarity-context construction (removed embedded instructions).
Action execution
kairon/actions/definitions/prompt.py
Refactored prompt assembly to iterate contexts, source system/user prompts from action config, build similarity prompts from similarity_config, removed embedding per-context instructions into similarity payload, and included user_prompt in final llmParams.
Media & chat integration
kairon/shared/chat/user_media.py
PromptAction instantiation now uses system_prompt (string) instead of constructing llm_prompts list; wiring updated to the new PromptAction fields.
Templates & fixtures
template/use-cases/Hi-Hello-GPT/actions.yml, tests/testing_data/actions/actions.yml, tests/testing_data/use-cases/Hi-Hello-GPT/actions.yml
Replaced llm_prompts blocks with contexts arrays and added system_prompt / user_prompt fields in fixtures and templates; removed embedded instruction texts in some contexts.
Processor / LLM plumbing
kairon/shared/llm/processor.py
Removed appending of similarity prompt instructions to similarity context; similarity prompt payload simplified.
Tests & test updates
tests/unit_test/action/action_test.py, tests/integration_test/action_service_test.py, tests/integration_test/services_test.py
Updated tests to construct and assert against contexts, system_prompt, and user_prompt instead of llm_prompts; expanded integration tests to reflect new prompt composition behavior.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Config as Action Config
    participant Model as PromptAction (model)
    participant Proc as Prompt Processor
    participant LLM as LLM Executor

    rect rgb(245,250,255)
    Config->>Model: define system_prompt, user_prompt, contexts[]
    end

    Model->>Proc: load action (system_prompt, user_prompt, contexts)
    loop for each context
        Proc->>Proc: read context.type / source / data / similarity_config
        alt similarity_config present
            Proc->>Proc: fetch top K similar docs, build similarity prompt entries
        else slot/crud/action source
            Proc->>Proc: build slot/crud/action-derived context payload
        end
    end

    Proc->>LLM: send llmParams {system_prompt, user_prompt, history, similarity_prompts, payload}
    alt success
        LLM->>Proc: response
    else error
        LLM->>Proc: error
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas that may need extra attention:
    • kairon/actions/definitions/prompt.py — ordering and composition of final llmParams (ensure user_prompt inclusion and removed instructions aren’t required elsewhere).
    • kairon/shared/utils.py & data_validation.py — verify validation semantics (system/history constraints) after system-prompt counting changes.
    • kairon/shared/data/processor.py & importer validator — confirm all call-sites and DB queries (contexts__*) were updated and maintain intended behavior.
    • tests — ensure behavioral expectations (not only schema) match new prompt assembly.

Possibly related PRs

Suggested reviewers

  • hiteshghuge
  • sfahad1414

Poem

🐰 I hopped through contexts, tidy and neat,
system and user now take their seat,
similarity whispers, instructions pared light,
slots and CRUD tiptoe into the night,
a rabbit cheers for prompts now right!

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'revamped prompt action' is directly related to the core changes in this PR, which involve restructuring the prompt action system from llm_prompts to contexts with system_prompt and user_prompt.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (7)
kairon/shared/llm/processor.py (1)

215-243: user_prompt is collected but never used in messages

You pass user_prompt through llm_params but __get_answer() ignores it. Include it in the user message so the new field actually affects completions.

Apply this diff:

@@
-        instructions = kwargs.get('instructions', [])
-        instructions = '\n'.join(instructions)
+        instructions_list = kwargs.get('instructions', [])
+        instructions = '\n'.join(instructions_list) if instructions_list else ''
+        user_prompt = kwargs.get('user_prompt', '')
@@
-        messages.append({"role": "user", "content": f"{context} \n{instructions} \nQ: {query} \nA:"}) if instructions \
-            else messages.append({"role": "user", "content": f"{context} \nQ: {query} \nA:"})
+        parts = []
+        if user_prompt:
+            parts.append(user_prompt)
+        parts.append(context)
+        if instructions:
+            parts.append(instructions)
+        parts.append(f"Q: {query} \nA:")
+        messages.append({"role": "user", "content": " \n".join(parts)})
kairon/shared/chat/user_media.py (1)

463-473: PromptAction creation missing required fields (user_prompt, contexts) — likely save failure

The model requires contexts and user_prompt. Create a minimal static context and set user_prompt to avoid ValidationError and to activate the media-extraction guidance.

Apply these diffs:

@@
-from kairon.shared.actions.data_objects import Actions, PromptAction
+from kairon.shared.actions.data_objects import Actions, PromptAction, Context
@@
-                prompt_action = PromptAction(
-                    name=action_name,
-                    system_prompt = f'{instructions[0]}',
-                    dispatch_response=True,
-                    process_media=True,
-                    bot=bot,
-                    status=True,
-                    user="system",
-                )
+                prompt_action = PromptAction(
+                    name=action_name,
+                    system_prompt=instructions[0],
+                    user_prompt=instructions[0],
+                    contexts=[
+                        Context(
+                            name="Media Context",
+                            data="Use the uploaded media (images, PDFs, docs) as primary context. If text is extracted, summarize it faithfully in markdown.",
+                            type="user",
+                            source="static",
+                            is_enabled=True,
+                        )
+                    ],
+                    dispatch_response=True,
+                    process_media=True,
+                    bot=bot,
+                    status=True,
+                    user="system",
+                )
kairon/shared/utils.py (1)

2187-2223: Clean up unused var and minor typo

  • system_prompt_count is unused.
  • Remove stray space in Utility.check_empty_string call.

Apply this diff:

@@
-        system_prompt_count = 0
-        history_prompt_count = 0
+        history_prompt_count = 0
@@
-            if Utility. check_empty_string(prompt.get("name")):
+            if Utility.check_empty_string(prompt.get("name")):
                 raise exception_class("Name cannot be empty!")
kairon/importer/validator/file_validator.py (1)

756-806: System prompt validation now fails for every prompt action
system_prompt_count is never incremented and the check for == 0 still runs inside the loop. That means we append “System prompt is required” on the very first context (and on every subsequent one), so every prompt-action import now fails—even when a system prompt exists via the new system_prompt field. Please restore the increment and move the count checks after the loop (or drop them if the top-level field replaces this requirement).

-        system_prompt_count = 0
+        system_prompt_count = 0
         history_prompt_count = 0
         for prompt in contexts:
+            if prompt.get('type') == 'system':
+                system_prompt_count += 1
             if prompt.get('similarity_config') is not None:
                 hyperparameters = prompt.get('similarity_config')
                 for key, value in hyperparameters.items():
@@
-            if system_prompt_count > 1:
-                error_list.append('Only one system prompt can be present')
-            if system_prompt_count == 0:
-                error_list.append('System prompt is required')
-            if history_prompt_count > 1:
-                error_list.append('Only one history source can be present')
+        if system_prompt_count > 1:
+            error_list.append('Only one system prompt can be present')
+        if system_prompt_count == 0:
+            error_list.append('System prompt is required')
+        if history_prompt_count > 1:
+            error_list.append('Only one history source can be present')
kairon/shared/data/processor.py (2)

7946-7979: Null/KeyError risks in __validate_llm_prompts (crud_config/data).

  • prompt["data"] may be missing → KeyError.
  • prompt.get("crud_config") can be None → AttributeError on .get.
  • Optional: validate CRUD query_source='slot' refers to an existing slot.
-    def __validate_llm_prompts(self, contexts, bot: Text):
-        for prompt in contexts:
-            if prompt["source"] == "slot":
-                if not Utility.is_exist(
-                        Slots, raise_error=False, name=prompt["data"], bot=bot, status=True
-                ):
-                    raise AppException(f'Slot with name {prompt["data"]} not found!')
-            if prompt["source"] == "action":
-                if not (
+    def __validate_llm_prompts(self, contexts, bot: Text):
+        for prompt in contexts or []:
+            source = (prompt or {}).get("source")
+            data = (prompt or {}).get("data")
+            if source == "slot":
+                if Utility.check_empty_string(data) or not Utility.is_exist(
+                        Slots, raise_error=False, name=data, bot=bot, status=True
+                ):
+                    raise AppException(f"Slot with name {data} not found!")
+            if source == "action":
+                if Utility.check_empty_string(data) or not (
                         Utility.is_exist(
                             HttpActionConfig,
                             raise_error=False,
-                            action_name=prompt["data"],
+                            action_name=data,
                             bot=bot,
                             status=True,
                         )
                         or Utility.is_exist(
                     GoogleSearchAction,
                     raise_error=False,
-                    name=prompt["data"],
+                    name=data,
                     bot=bot,
                     status=True,
                 )
                 ):
-                    raise AppException(f'Action with name {prompt["data"]} not found!')
+                    raise AppException(f"Action with name {data} not found!")
 
-            if prompt["source"] == "crud":
-                collections_list = DataProcessor.get_all_collections(bot)
+            if source == "crud":
+                collections_list = DataProcessor.get_all_collections(bot)
                 existing_collections = {item['collection_name'] for item in collections_list}
-                missing_collections = [col for col in prompt.get('crud_config').get("collections",[]) if col not in existing_collections]
+                crud_cfg = (prompt or {}).get('crud_config') or {}
+                missing_collections = [col for col in (crud_cfg.get("collections") or []) if col not in existing_collections]
 
                 if missing_collections:
                     raise AppException(f'Collections not found: {missing_collections}')
+                # optional: if query_source is slot, validate slot name exists
+                if crud_cfg.get("query_source") == "slot":
+                    qslot = crud_cfg.get("query")
+                    if Utility.check_empty_string(qslot) or not Utility.is_exist(
+                            Slots, raise_error=False, name=qslot, bot=bot, status=True
+                    ):
+                        raise AppException(f"Slot with name {qslot} not found for CRUD query.")

7931-7936: Normalize contexts to Context objects with field filtering to match edit_prompt_action behavior.

The inconsistency is confirmed: add_prompt_action passes raw dicts to PromptAction(**request_data) at line 7935, while edit_prompt_action explicitly builds Context objects at lines 8006-8008. Raw dicts with unexpected fields (e.g., "instructions") will cause MongoEngine errors. The suggested fix correctly normalizes and filters to the seven allowed Context fields.

-        self.__validate_llm_prompts(request_data.get("contexts", []), bot)
+        self.__validate_llm_prompts(request_data.get("contexts", []), bot)
+        # normalize contexts to EmbeddedDocuments and filter allowed fields
+        allowed_ctx_fields = {"name","data","type","source","is_enabled","crud_config","similarity_config"}
+        request_data["contexts"] = [
+            Context(**{k: v for k, v in (ctx or {}).items() if k in allowed_ctx_fields})
+            for ctx in (request_data.get("contexts") or [])
+        ]
kairon/shared/actions/data_objects.py (1)

819-837: Add validation for similarity_config placement.

The similarity_config field is added but lacks validation to ensure it's only provided when source is bot_content. Based on the logic in kairon/shared/data/data_models.py (lines 1138-1141), similarity_config should be removed when the source is not bot_content.

Apply this diff to add the missing validation:

         if self.source == LlmPromptSource.bot_content.value and Utility.check_empty_string(self.data):
             self.data = "default"
+        
+        if self.source == LlmPromptSource.bot_content.value:
+            if self.similarity_config:
+                self.similarity_config.validate()
+        else:
+            if self.similarity_config:
+                raise ValidationError("similarity_config should only be provided when source is 'bot_content'")
🧹 Nitpick comments (9)
kairon/shared/llm/processor.py (1)

1-7: Safer similarity_context assembly: avoid raw repr, adjust label, and default pop

  • Default pop to avoid KeyError when similarity_prompt missing.
  • Replace “Instructions…” label with neutral “Context…”.
  • Don’t dump Python list/dict repr into prompts; format as readable text (JSON or bullets).

Apply these diffs:

@@
-import os
-import time
+import os
+import time
+import json
@@
-        similarity_prompt = kwargs.pop('similarity_prompt')
+        similarity_prompt = kwargs.pop('similarity_prompt', [])
@@
-                if extracted_values:
-                    similarity_context = f"Instructions on how to use {similarity_prompt_name}:\n{extracted_values}\n"
-                    context_prompt = f"{context_prompt}\n{similarity_context}"
+                if extracted_values:
+                    if isinstance(extracted_values, list):
+                        flattened = "\n".join(
+                            [
+                                f"- {val}" if isinstance(val, str) else f"- {json.dumps(val, ensure_ascii=False)}"
+                                for val in extracted_values
+                            ]
+                        )
+                    else:
+                        flattened = str(extracted_values)
+                    similarity_context = f"Context from {similarity_prompt_name}:\n{flattened}\n"
+                    context_prompt = f"{context_prompt}\n{similarity_context}"

Also applies to: 408-437

kairon/shared/cognition/processor.py (1)

316-323: Tighten linked-action detection to bot_content contexts

Current filter may match unrelated static/slot data named like the collection. Add source='bot_content' to avoid false positives.

Apply this diff:

-        prompt_action = list(PromptAction.objects(bot=bot, contexts__data__iexact=collection))
+        prompt_action = list(
+            PromptAction.objects(
+                bot=bot,
+                contexts__source="bot_content",
+                contexts__data__iexact=collection,
+            )
+        )
kairon/shared/data/data_validation.py (2)

92-103: Guard contexts access and emit clear error when missing

Avoid KeyError and provide actionable feedback if contexts are absent.

Apply this diff:

-        llm_prompts_errors = DataValidation.validate_llm_prompts(data['contexts'])
-        data_error.extend(llm_prompts_errors)
+        contexts = data.get('contexts') or []
+        if not contexts:
+            data_error.append('contexts are required')
+        else:
+            llm_prompts_errors = DataValidation.validate_llm_prompts(contexts)
+            data_error.extend(llm_prompts_errors)

135-188: Align contexts validation with new model; fix f-string and add type checks

  • Don’t require a system context now that system_prompt is top-level.
  • Validate similarity_config type before iterating.
  • Fix extraneous f-string.

Apply this diff:

-        system_prompt_count = 0
-        history_prompt_count = 0
+        history_prompt_count = 0
@@
-            if prompt.get('similarity_config') is not None:
-                hyperparameters = prompt.get('similarity_config')
-                for key, value in hyperparameters.items():
+            cfg = prompt.get('similarity_config')
+            if cfg is not None:
+                if not isinstance(cfg, dict):
+                    error_list.append("similarity_config must be an object")
+                    cfg = {}
+                for key, value in cfg.items():
@@
-                            error_list.append(
-                                f"similarity_threshold should be within 0.3 and 1.0 and of type int or float!")
+                            error_list.append(
+                                "similarity_threshold should be within 0.3 and 1.0 and of type int or float!")
@@
-            if prompt.get('type') == 'system':
-                system_prompt_count += 1
-            elif prompt.get('source') == 'history':
+            if prompt.get('source') == 'history':
                 history_prompt_count += 1
@@
-            if prompt.get('type') == 'system' and prompt.get('source') != 'static':
-                error_list.append('System prompt must have static source')
+            # system prompt is provided via top-level system_prompt; no system context checks
@@
-            if system_prompt_count > 1:
-                error_list.append('Only one system prompt can be present')
-            if system_prompt_count == 0:
-                error_list.append('System prompt is required')
+            # no system context required/allowed checks here
             if history_prompt_count > 1:
                 error_list.append('Only one history source can be present')
tests/integration_test/services_test.py (3)

12408-12408: Remove commented-out instructions fields.

The commented-out instructions fields appear to be leftover from the refactoring where instructions were removed from the context structure. These should be cleaned up.

For example:

            {
                "name": "Similarity Prompt",
-               # 'instructions': 'Answer question based on the context above, if answer is not in the context go check previous logs.',
                'type': 'user',
                'source': 'bot_content',

Also applies to: 12412-12412, 12417-12417, 13143-13143, 13151-13151, 13189-13189, 13197-13197, 13243-13243, 13259-13259, 13292-13292, 13300-13300, 13339-13339, 13347-13347, 13355-13355, 13398-13398, 13406-13406, 13414-13414, 13453-13453, 13461-13461, 13469-13469, 13513-13513, 13521-13521, 13529-13529, 13749-13749, 13757-13757, 13800-13800, 13808-13808, 13816-13816, 13863-13863, 13871-13871, 13879-13879, 13924-13924, 13932-13932, 13940-13940, 13982-13982, 13990-13990, 13998-13998, 14031-14031, 14039-14039, 14047-14047, 14081-14081, 14089-14089, 14097-14097, 14132-14132, 14140-14140, 14174-14174, 14182-14182, 14218-14218, 14226-14226, 14266-14266, 14274-14274, 14321-14321, 14329-14329, 14337-14337, 14400-14400, 14408-14408, 14416-14416, 14451-14451, 14459-14459, 14467-14467, 14530-14530, 14538-14538, 14546-14546, 14598-14598, 14602-14602, 14607-14607, 14662-14662, 14670-14670, 14678-14678, 14732-14732, 14736-14736, 14741-14741, 14789-14789, 14797-14797, 14805-14805, 14852-14852, 14902-14902, 14953-14953, 15003-15003, 15056-15056, 36540-36540, 36548-36548, 36556-36556, 36778-36778, 36786-36786, 36794-36794


13131-13132: Inconsistent formatting: extra blank line after contexts opening bracket.

There are extra blank lines after the "contexts": [ opening bracket in many test cases. While not a functional issue, this creates inconsistent formatting across the test file.

Consider removing these extra blank lines for consistency:

-        "contexts": [
-
+        "contexts": [
             {

Also applies to: 13184-13185, 13335-13336, 13394-13395, 13449-13450, 13508-13509, 13620-13621, 13675-13676, 13734-13735, 13859-13860, 13920-13921, 13978-13979, 14027-14028, 14076-14077, 14127-14128, 14170-14171, 14214-14215, 14262-14263, 14317-14318, 14396-14397, 14447-14448, 14526-14527, 14658-14659, 14785-14786, 14849-14850, 14899-14900, 14950-14951, 15000-15001, 15053-15054, 15370-15371, 15407-15422, 36536-36537, 36774-36775


14617-14622: Remove commented-out code.

The commented-out print(actual) line should be removed.

-    # print(actual)
     response = client.post(
kairon/shared/data/processor.py (1)

380-383: Slot mapping misses CRUD query slot usage.

If a context has source='crud' with crud_config.query_source='slot', the slot is used but won’t be detected here. Add a check so get_slot_mapped_actions reports correct dependencies.

-        contexts = config.get("contexts", [])
-        for prompt in contexts:
+        contexts = config.get("contexts", [])
+        for prompt in contexts:
             if prompt.get('source') == "slot" and prompt.get('data') == slot:
                 return True
+            # also consider CRUD contexts where the query is sourced from a slot
+            if prompt.get('source') == "crud":
+                crud_cfg = prompt.get('crud_config') or {}
+                if crud_cfg.get('query_source') == 'slot' and crud_cfg.get('query') == slot:
+                    return True
kairon/shared/actions/data_objects.py (1)

857-857: Consider marking contexts as required.

The contexts field is validated as required in the validate method (line 877), but it's not marked as required=True at the schema level. According to the AI summary, the previous llm_prompts field was marked as required=True. For consistency and clarity, consider marking this field as required.

Apply this diff:

-    contexts = EmbeddedDocumentListField(Context)
+    contexts = EmbeddedDocumentListField(Context, required=True)

This change ensures the requirement is enforced at both the schema and validation levels.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d7ff65 and e175149.

📒 Files selected for processing (13)
  • kairon/actions/definitions/prompt.py (2 hunks)
  • kairon/importer/validator/file_validator.py (3 hunks)
  • kairon/shared/actions/data_objects.py (4 hunks)
  • kairon/shared/chat/user_media.py (1 hunks)
  • kairon/shared/cognition/processor.py (1 hunks)
  • kairon/shared/data/data_models.py (6 hunks)
  • kairon/shared/data/data_validation.py (3 hunks)
  • kairon/shared/data/processor.py (6 hunks)
  • kairon/shared/llm/processor.py (2 hunks)
  • kairon/shared/utils.py (3 hunks)
  • template/use-cases/Hi-Hello-GPT/actions.yml (1 hunks)
  • tests/integration_test/services_test.py (67 hunks)
  • tests/testing_data/actions/actions.yml (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
kairon/shared/utils.py (1)
kairon/shared/models.py (1)
  • LlmPromptSource (96-103)
tests/integration_test/services_test.py (3)
kairon/shared/data/data_objects.py (1)
  • BotSettings (903-949)
kairon/shared/utils.py (3)
  • Utility (93-2341)
  • get_default_llm_hyperparameters (2138-2139)
  • get_llm_hyperparameters (2142-2148)
kairon/shared/actions/data_objects.py (3)
  • PromptAction (847-887)
  • UserQuestion (839-842)
  • SetSlotsFromResponse (96-102)
kairon/shared/cognition/processor.py (1)
kairon/shared/actions/data_objects.py (1)
  • PromptAction (847-887)
kairon/shared/data/data_models.py (3)
kairon/shared/actions/data_objects.py (1)
  • Context (795-836)
kairon/shared/models.py (1)
  • LlmPromptSource (96-103)
kairon/shared/utils.py (3)
  • Utility (93-2341)
  • check_empty_string (148-160)
  • validate_llm_hyperparameters (2151-2163)
kairon/actions/definitions/prompt.py (2)
kairon/shared/models.py (2)
  • LlmPromptType (106-109)
  • LlmPromptSource (96-103)
kairon/shared/actions/utils.py (1)
  • prepare_bot_responses (263-281)
kairon/shared/actions/data_objects.py (2)
kairon/shared/data/data_models.py (1)
  • Context (1105-1144)
kairon/shared/utils.py (2)
  • Utility (93-2341)
  • validate_kairon_faq_llm_prompts (2187-2223)
kairon/shared/data/data_validation.py (1)
kairon/shared/data/data_models.py (1)
  • validate_llm_prompts (1189-1195)
kairon/shared/data/processor.py (3)
kairon/shared/actions/data_objects.py (3)
  • Context (795-836)
  • PromptAction (847-887)
  • UserQuestion (839-842)
kairon/shared/data/data_models.py (1)
  • Context (1105-1144)
kairon/shared/models.py (1)
  • LlmPromptSource (96-103)
🪛 Ruff (0.14.3)
kairon/shared/utils.py

2188-2188: Local variable system_prompt_count is assigned to but never used

Remove assignment to unused variable system_prompt_count

(F841)

kairon/shared/data/data_models.py

1209-1209: Avoid specifying long messages outside the exception class

(TRY003)


1213-1213: Avoid specifying long messages outside the exception class

(TRY003)


1216-1216: Prefer TypeError exception for invalid type

(TRY004)


1216-1216: Avoid specifying long messages outside the exception class

(TRY003)

kairon/shared/actions/data_objects.py

878-878: Avoid specifying long messages outside the exception class

(TRY003)

kairon/shared/data/data_validation.py

147-147: f-string without any placeholders

Remove extraneous f prefix

(F541)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Python CI
  • GitHub Check: Analyze (python)
🔇 Additional comments (13)
template/use-cases/Hi-Hello-GPT/actions.yml (1)

28-37: LGTM on migrating to system_prompt/user_prompt + contexts

Template matches the new config shape and defaults are reasonable.

tests/testing_data/actions/actions.yml (1)

266-278: Confirm no duplicate system instructions at runtime

You now have top-level system_prompt and also a system context here. Ensure the system context isn’t concatenated into messages to avoid duplication. If unnecessary, consider removing it from tests for clarity.

Also applies to: 291-302

kairon/actions/definitions/prompt.py (2)

155-157: user_prompt passthrough is fine; ensure the runtime uses it

You correctly add user_prompt to llmParams. With the proposed LLMProcessor.__get_answer change, it will be included in the user message.

Also applies to: 235-237


158-175: Similarity prompt config read looks good

Using similarity_config with sane defaults (top_results=10, threshold=0.7) aligns with the refactor goals.

tests/integration_test/services_test.py (4)

15370-15392: Test setup uses direct database insertion with contexts.

These test setup sections directly create PromptAction objects with the new contexts structure and system_prompt/user_prompt fields. The structure looks correct and consistent with the data model changes.

Also applies to: 15395-15405, 15407-15445


14845-14868: New test coverage for 'crud' source type.

These tests validate the new 'crud' source type for contexts, including validation of query formats (JSON object, string query, slot-based query, and invalid JSON). This provides good coverage for the new functionality.

Also applies to: 14895-14918, 14946-14969, 14996-15019, 15049-15072


13445-13500: Test is incorrect—wrong data structure being tested.

The test passes a list for system_prompt field (which expects str per the Pydantic model), but expects an error about the contexts array. When Pydantic validates system_prompt as a list, it will reject at the field level with error location ["body", "system_prompt"], not ["body", "contexts"].

The error message "Only one system prompt can be present!" refers to validation of multiple system-type prompts within the contexts array, not the system_prompt field type. To properly test that validation, pass multiple context objects with "type": "system", not a list value for the system_prompt field. Either fix the test data to match the expected error location/message, or adjust expectations to match what a list value for system_prompt actually produces.

Likely an incorrect or invalid review comment.


13224-13225: Error message validations confirmed—no issues found.

All error message assertions in the integration tests are correctly aligned with backend Pydantic validators in kairon/shared/data/data_models.py:

  • similarity_threshold (line 13224-13225): Test expects 'similarity_threshold should be within 0.3 and 1' — matches validator at line 1093 ✓
  • top_results (line 13278-13279): Test expects 'top_results should not be greater than 30' — matches validator at line 1095 ✓
  • system_prompt (line 13435-13436): Test expects 'System prompt cannot be empty!' — matches dynamic error at line 1213 ✓
  • user_prompt (line 13552-13553): Test expects 'User prompt cannot be empty!' — matches dynamic error at line 1213 ✓

The error messages are dynamically generated at line 1213 using f"{field.name.replace('_', ' ').capitalize()} cannot be empty!", correctly producing the expected formats.

kairon/shared/data/processor.py (3)

83-83: Import of Context looks good.

Aligns this module with the new Context model.


704-706: Good guard to prevent deleting actions referenced by PromptAction contexts.

The contexts__source/contexts__data filter matches the new schema.


7998-7999: LGTM on switching validation to contexts.

Call site updated correctly.

kairon/shared/actions/data_objects.py (2)

862-862: LGTM!

The process_media field addition is straightforward with a sensible default value.


858-859: LGTM!

The addition of system_prompt and user_prompt as required fields provides a clearer structure compared to the previous instructions field.

Comment on lines +877 to 878
if not self.contexts:
raise ValidationError("llm_prompts are required!")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Update error message to reflect the new field name.

The error message references the old field name llm_prompts, but the field has been renamed to contexts. This inconsistency could confuse users.

Apply this diff to fix the error message:

         if not self.contexts:
-            raise ValidationError("llm_prompts are required!")
+            raise ValidationError("contexts are required!")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if not self.contexts:
raise ValidationError("llm_prompts are required!")
if not self.contexts:
raise ValidationError("contexts are required!")
🧰 Tools
🪛 Ruff (0.14.3)

878-878: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
In kairon/shared/actions/data_objects.py around lines 877 to 878, the
ValidationError message still references the old field name "llm_prompts";
update the exception text to reference the renamed field "contexts" instead
(e.g., raise ValidationError("contexts are required!")) so the error message
matches the current field name.

Comment on lines +8006 to 8011
action.contexts = [
Context(**prompt) for prompt in request_data.get("contexts", [])
]
action.instructions = request_data.get("instructions", [])
action.system_prompt = request_data.get("system_prompt", [])
action.user_prompt = request_data.get("user_prompt", [])
action.set_slots = request_data.get("set_slots", [])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Wrong defaults and potential FieldDoesNotExist when updating contexts; fix type of prompts.

  • action.system_prompt/user_prompt default to [] (should be str). Use presence checks.
  • Context(**prompt) may break on unknown keys. Filter to allowed fields, as in creation.
-        action.contexts = [
-            Context(**prompt) for prompt in request_data.get("contexts", [])
-        ]
-        action.system_prompt = request_data.get("system_prompt", [])
-        action.user_prompt = request_data.get("user_prompt", [])
+        allowed_ctx_fields = {"name","data","type","source","is_enabled","crud_config","similarity_config"}
+        if "contexts" in request_data:
+            action.contexts = [
+                Context(**{k: v for k, v in (prompt or {}).items() if k in allowed_ctx_fields})
+                for prompt in (request_data.get("contexts") or [])
+            ]
+        if "system_prompt" in request_data:
+            action.system_prompt = request_data["system_prompt"]
+        if "user_prompt" in request_data:
+            action.user_prompt = request_data["user_prompt"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
action.contexts = [
Context(**prompt) for prompt in request_data.get("contexts", [])
]
action.instructions = request_data.get("instructions", [])
action.system_prompt = request_data.get("system_prompt", [])
action.user_prompt = request_data.get("user_prompt", [])
action.set_slots = request_data.get("set_slots", [])
allowed_ctx_fields = {"name","data","type","source","is_enabled","crud_config","similarity_config"}
if "contexts" in request_data:
action.contexts = [
Context(**{k: v for k, v in (prompt or {}).items() if k in allowed_ctx_fields})
for prompt in (request_data.get("contexts") or [])
]
if "system_prompt" in request_data:
action.system_prompt = request_data["system_prompt"]
if "user_prompt" in request_data:
action.user_prompt = request_data["user_prompt"]
action.set_slots = request_data.get("set_slots", [])
🤖 Prompt for AI Agents
In kairon/shared/data/processor.py around lines 8006–8011, the code assigns
defaults incorrectly and risks FieldDoesNotExist when updating contexts; change
system_prompt and user_prompt to only be set when present (use request_data.get
and check for None/exists) so they remain strings (or None) rather than
defaulting to [] and keep set_slots as is; when constructing Context objects
from request_data["contexts"], filter each prompt dict to only the allowed
Context fields (use the same allowed_fields list used in creation or Context
model field names) before calling Context(**filtered_prompt) to avoid
unknown-key errors.

},
{
"name": "Query Prompt",
"data": "If there is no specific query, assume that user is aking about java programming.",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix repeated typo across multiple test cases.

The word "aking" should be "asking" in multiple test data strings.

This typo appears in approximately 20 locations throughout the test file. Apply similar fixes to all occurrences:

-               'data': 'If there is no specific query, assume that user is aking about java programming.',
+               'data': 'If there is no specific query, assume that user is asking about java programming.',

Also applies to: 13258-13258, 13354-13354, 13468-13468, 13528-13528, 13587-13587, 13691-13691, 13757-13757, 13815-13815, 13877-13877, 14139-14139, 14181-14181, 14336-14336, 14415-14415, 14466-14466, 14545-14545, 14606-14606, 14677-14677, 14740-14740, 14804-14804

bot_settings = BotSettings.objects(bot=pytest.bot).get()
bot_settings.llm_settings.enable_faq = True
bot_settings.save()
print(bot_settings.to_json())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Remove debug print statements.

Debug print statements should be removed from test code before merging.

Apply this diff to remove the print statements:

-    print(bot_settings.to_json())
-    print(actual)

Also applies to: 13483-13483, 13505-13505, 13543-13543, 14635-14635

🤖 Prompt for AI Agents
In tests/integration_test/services_test.py around lines 13390, 13483, 13505,
13543, and 14635 there are leftover debug print() calls (e.g.,
print(bot_settings.to_json())) that must be removed; edit each listed line to
delete the print statement so tests don't emit debug output and commit the
changes.

Comment on lines 13482 to 13483
bot_settings = BotSettings.objects(bot=pytest.bot).get()
print(bot_settings.to_json())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Remove duplicate bot_settings retrieval.

The bot_settings object is retrieved and printed twice consecutively, which is redundant.

Apply this diff:

     }
-    bot_settings = BotSettings.objects(bot=pytest.bot).get()
-    print(bot_settings.to_json())
     response = client.post(

Also applies to: 13542-13543

🤖 Prompt for AI Agents
In tests/integration_test/services_test.py around lines 13482-13483 and
13542-13543, the BotSettings.objects(bot=pytest.bot).get() call is duplicated
and printed twice; remove the redundant retrieval and print so bot_settings is
retrieved once and reused. Replace the second duplicate lines in each location
with nothing (delete them) or reference the existing bot_settings variable
instead of re-querying, ensuring no behavioral change to the test.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
tests/unit_test/action/action_test.py (2)

4760-4766: Use ContextDoc for MongoEngine validations

After aliasing, update MongoEngine-based tests to use ContextDoc.

Apply this diff:

-        prompt = Context(
+        prompt = ContextDoc(
@@
-        prompt = Context(
+        prompt = ContextDoc(

Also applies to: 4774-4785


4802-4809: Use ContextModel for Pydantic validations

Swap constructor calls to the aliased Pydantic class.

Apply this diff:

-            Context(
+            ContextModel(
@@
-            Context(
+            ContextModel(
@@
-            Context(
+            ContextModel(

Also applies to: 4821-4829, 4841-4845

♻️ Duplicate comments (2)
tests/integration_test/services_test.py (2)

13380-13390: Remove leftover debug print statements.

The print(...) calls (for example, Lines 13380, 13467, 13524, and 14581) were already flagged and still need to be dropped so the integration tests stay clean.

Apply this diff pattern:

-    print(bot_settings.to_json())-    print(actual)

Also applies to: 13466-13474, 13524-13532, 14580-14582


12416-12419: Fix repeated “aking” typo in prompt data.

The sample prompt text still says “aking” instead of “asking”. Please update this string everywhere it appears so the test fixtures read correctly.

Apply this representative diff (repeat for the other occurrences):

-                "data": "If there is no specific query, assume that user is aking about java programming.",
+                "data": "If there is no specific query, assume that user is asking about java programming.",

Also applies to: 13148-13152, 13252-13256, 13344-13348, 13400-13404, 13502-13512, 13734-13738, 13790-13794, 13850-13854, 13908-13912, 13964-13968, 14008-14012, 14056-14060, 14136-14142, 14226-14230, 14284-14288, 14363-14368, 14412-14416, 14622-14627, 14749-14754

🧹 Nitpick comments (1)
tests/unit_test/action/action_test.py (1)

4794-4795: Remove duplicate local imports of Pydantic Context

Once aliased at the top, these re-imports are unnecessary and risk confusion. Use ContextModel directly.

Apply this diff:

-from kairon.shared.data.data_models import Context
+# use ContextModel from top-level import
@@
-from kairon.shared.data.data_models import Context
+# use ContextModel from top-level import
@@
-from kairon.shared.data.data_models import Context
+# use ContextModel from top-level import

Also applies to: 4812-4814, 4831-4833

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e175149 and 302e89c.

📒 Files selected for processing (3)
  • kairon/shared/data/data_validation.py (3 hunks)
  • tests/integration_test/services_test.py (64 hunks)
  • tests/unit_test/action/action_test.py (12 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • kairon/shared/data/data_validation.py
🧰 Additional context used
🧬 Code graph analysis (2)
tests/integration_test/services_test.py (2)
kairon/shared/data/data_objects.py (1)
  • BotSettings (903-949)
kairon/shared/utils.py (3)
  • Utility (93-2341)
  • get_default_llm_hyperparameters (2138-2139)
  • get_llm_hyperparameters (2142-2148)
tests/unit_test/action/action_test.py (2)
kairon/shared/data/data_models.py (2)
  • Context (1105-1144)
  • CrudConfigRequest (1099-1103)
kairon/shared/actions/data_objects.py (3)
  • Context (795-836)
  • CrudConfig (772-793)
  • PromptAction (847-887)
🪛 Ruff (0.14.3)
tests/unit_test/action/action_test.py

49-49: Redefinition of unused Context from line 12

(F811)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Analyze (python)
  • GitHub Check: Python CI
🔇 Additional comments (4)
tests/unit_test/action/action_test.py (4)

3311-3323: LGTM: contexts + system/user prompts setup

The new contexts array and separate system_prompt/user_prompt align with the updated model. Save flow looks correct.


3338-3351: LGTM: assertions match new payload shape

Assertions cover contexts with similarity_config and the new system_prompt/user_prompt fields.

Consider adding one negative assertion to ensure no legacy llm_prompts key exists in the payload.


4701-4712: LGTM: second prompt action uses new schema

Contexts, system_prompt, and user_prompt are correctly populated before retrieval.


4724-4734: LGTM: config assertions reflect new fields

Config check validates contexts and both prompt fields; consistent with data model changes.

from kairon.exceptions import AppException
from kairon.shared.admin.data_objects import LLMSecret
from kairon.shared.data.data_models import LlmPromptRequest, CrudConfigRequest
from kairon.shared.data.data_models import Context, CrudConfigRequest
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix Context import collision (ruff F811) by aliasing both types

Two different Context classes are imported, the second redefines the first. Alias them to avoid shadowing and confusion across tests.

Apply this diff:

-from kairon.shared.data.data_models import Context, CrudConfigRequest
+from kairon.shared.data.data_models import Context as ContextModel, CrudConfigRequest
@@
-from kairon.shared.actions.data_objects import HttpActionRequestBody, HttpActionConfig, ActionServerLogs, SlotSetAction, \
+from kairon.shared.actions.data_objects import HttpActionRequestBody, HttpActionConfig, ActionServerLogs, SlotSetAction, \
     Actions, FormValidationAction, EmailActionConfig, GoogleSearchAction, JiraAction, ZendeskAction, \
     PipedriveLeadsAction, SetSlots, HubspotFormsAction, HttpActionResponse, CustomActionRequestParameters, \
     KaironTwoStageFallbackAction, SetSlotsFromResponse, PromptAction, PyscriptActionConfig, WebSearchAction, \
-    CustomActionParameters, ParallelActionConfig, DatabaseAction, CrudConfig, Context
+    CustomActionParameters, ParallelActionConfig, DatabaseAction, CrudConfig, Context as ContextDoc

Also applies to: 49-49

🤖 Prompt for AI Agents
In tests/unit_test/action/action_test.py around lines 12 and 49, two different
Context classes are being imported and the second import shadows the first (ruff
F811); update the imports to alias each Context to distinct names (e.g.,
ContextModel and ContextDto or similar) and then update all usages in this file
to use the new aliases so there is no name collision.

Comment on lines +4876 to 4880
contexts = [
{
'source': 'static', 'is_enabled': True, 'collections': [], 'result_limit': 10, 'query': {}},
crud_prompt
]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Fix malformed static context in request (missing name/type; CRUD-only keys present)

The first entry under contexts lacks required fields (name, type) and includes CRUD-only keys. It will fail validation before your missing-collection check.

Apply this diff to define a valid static context (or remove it if not needed):

-        contexts = [
-            {
-             'source': 'static', 'is_enabled': True, 'collections': [], 'result_limit': 10, 'query': {}},
-            crud_prompt
-        ]
+        contexts = [
+            {'name': 'Static Prompt', 'type': 'user', 'source': 'static', 'is_enabled': True},
+            crud_prompt
+        ]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
contexts = [
{
'source': 'static', 'is_enabled': True, 'collections': [], 'result_limit': 10, 'query': {}},
crud_prompt
]
contexts = [
{'name': 'Static Prompt', 'type': 'user', 'source': 'static', 'is_enabled': True},
crud_prompt
]
🤖 Prompt for AI Agents
In tests/unit_test/action/action_test.py around lines 4876 to 4880, the first
contexts entry is malformed: it lacks required fields (name and type) and
contains CRUD-only keys (collections, result_limit, query) which cause
validation to fail before the missing-collection assertion runs; replace that
entry with a valid static context (or remove it) by providing required fields
(e.g., name and type='static') and remove CRUD-only keys or move them into the
appropriate CRUD context (or set the static context to an empty/static payload
that passes schema validation) so the test proceeds to the intended
missing-collection check.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 302e89c and 7546006.

📒 Files selected for processing (3)
  • tests/integration_test/action_service_test.py (47 hunks)
  • tests/testing_data/actions/actions.yml (2 hunks)
  • tests/testing_data/use-cases/Hi-Hello-GPT/actions.yml (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/integration_test/action_service_test.py (1)
kairon/shared/actions/data_objects.py (2)
  • PromptAction (847-887)
  • UserQuestion (839-842)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Analyze (python)
  • GitHub Check: Python CI
🔇 Additional comments (2)
tests/testing_data/actions/actions.yml (1)

266-295: Verify the "instructions" field consistency across context definitions.

The first context (lines 267-272) includes an instructions field, but the second context (lines 289-293) does not. If instructions is an optional field in the Context model, this is acceptable; otherwise, this could indicate incomplete test data coverage.

Can you confirm whether:

  1. The instructions field is optional in the Context class?
  2. If optional, both test contexts should be equally valid with or without it?
  3. Whether context type (query vs user) determines whether instructions should be present?

Additionally, verify that test cases cover both scenarios (with and without instructions) to ensure the prompt assembly logic handles both correctly.

tests/testing_data/use-cases/Hi-Hello-GPT/actions.yml (1)

28-37: Verify slot-based context data reference and multi-line prompt formatting.

The context uses source: slot with data: google_search_result. Ensure this slot name is properly defined in the bot's slot configuration and is resolved correctly during prompt assembly.

Additionally, verify that the multi-line system_prompt (lines 35-36) is parsed correctly by the YAML parser and that whitespace/line breaks are handled as intended in the LLM request payload.

Can you confirm:

  1. That google_search_result is a valid slot name defined elsewhere in the test fixtures?
  2. That the prompt payload correctly includes the multi-line system prompt without unintended whitespace artifacts?

Comment on lines +12381 to +12382
user_prompt="Answer in a short manner. Keep it simple.",
system_prompt= "You are a personal assistant.",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove trailing commas that turn prompts into tuples

Each user_prompt / system_prompt assignment ends with a trailing comma, so Python stores them as one-element tuples instead of strings. PromptAction expects StringField values, and the tuple breaks validation (or later string operations). Clean this up everywhere this pattern appears in the file—for example:

-    user_prompt="Answer in a short manner. Keep it simple.",
-    system_prompt= "You are a personal assistant.",
+    user_prompt="Answer in a short manner. Keep it simple.",
+    system_prompt= "You are a personal assistant."

(Repeat for every other user_prompt / system_prompt assignment that currently ends with a comma.)

Also applies to: 12470-12471, 12701-12702, 12795-12796, 12892-12893

🤖 Prompt for AI Agents
In tests/integration_test/action_service_test.py around lines 12381-12382 (and
also clean up at 12470-12471, 12701-12702, 12795-12796, 12892-12893), remove the
trailing commas after user_prompt and system_prompt assignments so the values
are plain strings instead of one-element tuples; update each occurrence by
deleting the comma at the end of the assignment line so PromptAction receives a
string and validation/string operations work correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant