Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions apps/fred-agents/fred_agents/general_assistant.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,11 @@
FieldSpec,
MCPServerRef,
UIHints,
apply_global_base_prompts,
)
from fred_sdk.contracts.models import ReActAgentDefinition, ReActPolicy

_SYSTEM_PROMPT_EN = """\
_BASE_SYSTEM_PROMPT_EN = """\
You are a helpful, knowledgeable, and concise assistant.
Answer questions clearly and directly. When you are uncertain, say so.

Expand All @@ -62,7 +63,7 @@
— do not pretend to have access to a document corpus or live data you cannot reach.
"""

_SYSTEM_PROMPT_FR = """\
_BASE_SYSTEM_PROMPT_FR = """\
Tu es un assistant serviable, compétent et concis.
Réponds aux questions clairement et directement. Lorsque tu n'es pas certain, dis-le.

Expand All @@ -73,6 +74,8 @@
à des données en temps réel que tu ne peux pas atteindre.
"""

_SYSTEM_PROMPT_EN = apply_global_base_prompts(_BASE_SYSTEM_PROMPT_EN)
_SYSTEM_PROMPT_FR = apply_global_base_prompts(_BASE_SYSTEM_PROMPT_FR)
_SYSTEM_PROMPT = _SYSTEM_PROMPT_EN


Expand Down
11 changes: 7 additions & 4 deletions apps/fred-agents/fred_agents/rag_expert.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,16 @@
GuardrailDefinition,
ToolRefRequirement,
UIHints,
apply_global_base_prompts,
load_agent_prompt_markdown,
)
from fred_sdk.contracts.models import ReActAgentDefinition, ReActPolicy
from fred_sdk.resources import load_agent_prompt_markdown

_RAG_EXPERT_SYSTEM_PROMPT: str = load_agent_prompt_markdown(
package="fred_agents.rag_expert",
file_name="basic_react_rag_expert_system_prompt.md",
_RAG_EXPERT_SYSTEM_PROMPT: str = apply_global_base_prompts(
load_agent_prompt_markdown(
package="fred_agents.rag_expert",
file_name="basic_react_rag_expert_system_prompt.md",
)
)


Expand Down
5 changes: 4 additions & 1 deletion apps/fred-agents/fred_agents/react_rag_mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,11 @@
FieldSpec,
MCPServerRef,
UIHints,
apply_global_base_prompts,
)
from fred_sdk.contracts.models import ReActAgentDefinition, ReActPolicy

_SYSTEM_PROMPT = """\
_BASE_SYSTEM_PROMPT = """\
You are a document-grounded assistant. Your answers must be grounded in \
retrieved documents, not in your training knowledge.

Expand All @@ -63,6 +64,8 @@
- Today is {today}.
"""

_SYSTEM_PROMPT = apply_global_base_prompts(_BASE_SYSTEM_PROMPT)


class ReactRagMcpDefinition(ReActAgentDefinition):
"""
Expand Down
11 changes: 7 additions & 4 deletions apps/fred-agents/fred_agents/sentinel.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@
FieldSpec,
MCPServerRef,
UIHints,
apply_global_base_prompts,
load_agent_prompt_markdown,
)
from fred_sdk.contracts.models import ReActAgentDefinition, ReActPolicy
from fred_sdk.resources import load_agent_prompt_markdown


class SentinelReActDefinition(ReActAgentDefinition):
Expand Down Expand Up @@ -63,9 +64,11 @@ class SentinelReActDefinition(ReActAgentDefinition):
"and platform KPI review."
)
tags: tuple[str, ...] = ("monitoring", "react")
system_prompt_template: str = load_agent_prompt_markdown(
package="fred_agents.sentinel",
file_name="basic_react_sentinel_system_prompt.md",
system_prompt_template: str = apply_global_base_prompts(
load_agent_prompt_markdown(
package="fred_agents.sentinel",
file_name="basic_react_sentinel_system_prompt.md",
)
)
default_mcp_servers: tuple[MCPServerRef, ...] = (
MCPServerRef(id=MCP_SERVER_KNOWLEDGE_FLOW_OPENSEARCH_OPS, locked=True),
Expand Down
11 changes: 7 additions & 4 deletions apps/fred-agents/fred_agents/sql_expert.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@
GuardrailDefinition,
MCPServerRef,
UIHints,
apply_global_base_prompts,
load_agent_prompt_markdown,
)
from fred_sdk.contracts.models import ReActAgentDefinition, ReActPolicy
from fred_sdk.resources import load_agent_prompt_markdown


class SqlExpertReActDefinition(ReActAgentDefinition):
Expand All @@ -39,9 +40,11 @@ class SqlExpertReActDefinition(ReActAgentDefinition):
"tabular tools, and answers from the results."
)
tags: tuple[str, ...] = ("sql", "tabular", "react")
system_prompt_template: str = load_agent_prompt_markdown(
package="fred_agents.sql_expert",
file_name="basic_react_sql_expert_system_prompt.md",
system_prompt_template: str = apply_global_base_prompts(
load_agent_prompt_markdown(
package="fred_agents.sql_expert",
file_name="basic_react_sql_expert_system_prompt.md",
)
)

default_mcp_servers: tuple[MCPServerRef, ...] = (
Expand Down
37 changes: 37 additions & 0 deletions apps/fred-agents/tests/test_prompting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from fred_agents.general_assistant import GENERAL_ASSISTANT_AGENT
from fred_agents.rag_expert import RAG_EXPERT_AGENT
from fred_agents.react_rag_mcp import REACT_RAG_MCP_AGENT
from fred_agents.sentinel import SENTINEL_AGENT
from fred_agents.sql_expert import SQL_EXPERT_AGENT

_EXPECTED_FRAGMENT = "When you include Mermaid diagrams, follow these rules strictly so the diagram always parses:"
_EXPECTED_FALLBACK_RULE = "If you are unsure the Mermaid will parse, do not return Mermaid, return a simpler Markdown list or table instead."


def test_all_base_agents_include_global_base_prompt_contract() -> None:
"""
Verify every shipped fred-agents base prompt includes the global Mermaid rules.

Why this test exists:
- the Mermaid output contract should apply uniformly to every default agent
- one regression in prompt composition would otherwise silently reintroduce
broken diagrams for some templates only

How to use it:
- run via the default fred-agents test suite

Example:
- `pytest tests/test_prompting.py -q`
"""

prompts = (
GENERAL_ASSISTANT_AGENT.system_prompt_template,
RAG_EXPERT_AGENT.system_prompt_template,
REACT_RAG_MCP_AGENT.system_prompt_template,
SENTINEL_AGENT.system_prompt_template,
SQL_EXPERT_AGENT.system_prompt_template,
)

for prompt in prompts:
assert _EXPECTED_FRAGMENT in prompt
assert _EXPECTED_FALLBACK_RULE in prompt
2 changes: 1 addition & 1 deletion apps/frontend/public/config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"frontend_basename": "/",
"user_auth": {
"enabled": false,
"enabled": true,
"realm_url": "http://app-keycloak:8080/realms/app",
"client_id": "app"
}
Expand Down
6 changes: 6 additions & 0 deletions docs/swift/backlog/CHAT-UI-BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,12 @@ The preview uses a single source-first shell for all open fences:
Once a Mermaid fence closes, the final markdown pipeline mounts `MermaidBlock`
and renders the SVG.

Companion hardening under the same execution issue: the standalone `fred-agents`
pod now packages one shared Mermaid output contract and appends it to every
shipped default agent prompt via SDK prompt-composition helpers. This does not
replace the frontend guard; it lowers the chance that the final closed fence is
invalid Mermaid in the first place.

### 10.2 Tasks

#### Step 1 — `streamingGuard` utility
Expand Down
24 changes: 23 additions & 1 deletion docs/swift/rfc/CHAT-RENDERING-SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,27 @@ Le prompt système de l'agent de référence (`fred.github.assistant`) vit dans
apps/fred-agents/fred_agents/general_assistant.py (constante _SYSTEM_PROMPT)
```

Les autres agents ont leur propre prompt dans leurs fichiers respectifs sous `apps/fred-agents/fred_agents/`. Le prompt est exposé comme champ `prompts.system` (type `prompt`) dans le formulaire de configuration de l'agent, ce qui permet à un administrateur de l'override depuis l'interface sans modifier le code.
Les autres agents ont leur propre prompt dans leurs fichiers respectifs sous
`apps/fred-agents/fred_agents/`.

Cible d'architecture :

- les fragments de prompt partagés entre plusieurs pods agents vivent dans une
lib sous `libs/`, idéalement côté `fred-sdk` (ou un package partagé voisin),
pas dans un package applicatif
- `fred-sdk` porte la surface d'authoring et de chargement de ces fragments
partagés via ses loaders Markdown packagés déjà existants
- chaque pod agent choisit explicitement quels fragments partagés il compose
avec ses prompts par défaut, puis peut ajouter ses fragments locaux au-dessus
- `fred-runtime` n'est pas le lieu de stockage de ces fragments réutilisables ;
il garde uniquement les injections runtime obligatoires et non overrideables
(ex. `agent_instructions` MCP)

Important : cette couche de prompt partagée sert à des conventions de sortie
transverses, par exemple un contrat de sortie Mermaid parse-safe utilisé par
plusieurs agents. Les contraintes non négociables liées à un outil restent dans
les contrats runtime / catalog (`agent_instructions`), pas dans ce socle de
prompt.

---

Expand All @@ -238,6 +258,8 @@ Tous les chemins sont relatifs à la racine du dépôt.
| `apps/frontend/src/styles/typography.css` | Tokens `--font-*` |
| `apps/frontend/src/styles/radius.css` | Token `--radius-s` = 8 px (utilisé par CodeBlock et MermaidBlock) |
| `apps/fred-agents/fred_agents/general_assistant.py` | Prompt système de l'agent de référence (`_SYSTEM_PROMPT`) |
| `libs/fred-sdk/fred_sdk/resources/prompts.py` | Loader Markdown partagé et helper de composition des fragments cross-pod |
| `libs/fred-sdk/fred_sdk/resources/prompts/mermaid_output_contract.md` | Contrat de sortie Mermaid parse-safe partagé par les agents par défaut |

---

Expand Down
41 changes: 40 additions & 1 deletion docs/swift/rfc/SDK-V2-RFC.md
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,46 @@ The template's `role` field is the catalog label (what the operator sees when
browsing templates). The `display_name` entered at enrollment becomes the instance's
identity within the team. These are two separate fields and must never be conflated.

### 18.4 Implementation contract for locked MCP servers
### 18.4 Shared prompt bundles in libs

Some output conventions should be reusable across pods, not reimplemented
inside one application package. A current example is Mermaid-safe Markdown
generation for the chat renderer: every shipped default agent should follow the
same formatting rules so the frontend can render diagrams reliably.

Decision:

- The canonical location for cross-pod shared prompt fragments is a library
package under `libs/`, with `fred-sdk` as the default authoring-facing
surface.
- No heavy framework feature is required for this. The existing
`fred_sdk.resources` Markdown loaders are already the right primitive; if one
extra helper is needed it should stay small and live in `fred-sdk`, not in an
application pod package.
- The concrete SDK helper is `fred_sdk.apply_global_base_prompts(...)`; it
appends the package-owned fragments from `fred_sdk.resources.prompts` to one
agent's local system prompt.
- A pod package may still add pod-local fragments, but it should do so by
extending the shared library bundle rather than by becoming the canonical
owner of cross-agent fragments.
- The shared bundle is **authoring-time composition of template defaults**. It
is not a runtime injection layer and does not replace operator overrides.
- Shared prompt bundles are for cross-agent presentation or output conventions
only.
- `fred-runtime` is not the home of these reusable fragments. Runtime remains
responsible for mandatory execution-time instructions and contract-enforced
injections.
- Tool-specific, non-negotiable behavior still belongs in runtime-enforced
contracts such as MCP `agent_instructions`, not in shared prompt layers.

This keeps author ergonomics simple while preserving the architectural rule of
this RFC: prompts may shape presentation, but operational correctness should
move toward explicit SDK or runtime contracts whenever possible. In practice, a
pod shipped elsewhere should be able to import the same shared prompt bundle,
append one or two pod-local fragments, and benefit from the same renderer-
oriented defaults without depending on any application pod package.

### 18.5 Implementation contract for locked MCP servers

The `locked: bool = False` field on `MCPServerRef` (fred-sdk) marks a server as
non-toggleable. A locked server:
Expand Down
7 changes: 6 additions & 1 deletion libs/fred-sdk/fred_sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,11 @@
# ---------------------------------------------------------------------------
# Resource loading helpers
# ---------------------------------------------------------------------------
from fred_sdk.resources import load_agent_prompt_markdown, load_packaged_markdown
from fred_sdk.resources import (
apply_global_base_prompts,
load_agent_prompt_markdown,
load_packaged_markdown,
)

# ---------------------------------------------------------------------------
# Built-in tool references
Expand Down Expand Up @@ -240,6 +244,7 @@
"RuntimeContext",
"ToolContentKind",
# Resource helpers
"apply_global_base_prompts",
"load_agent_prompt_markdown",
"load_packaged_markdown",
# Built-in tool references
Expand Down
7 changes: 6 additions & 1 deletion libs/fred-sdk/fred_sdk/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@
- `system_prompt = load_packaged_markdown(package=\"my_pkg\", path_parts=(\"prompts\", \"system.md\"))`
"""

from .prompts import load_agent_prompt_markdown, load_packaged_markdown
from .prompts import (
apply_global_base_prompts,
load_agent_prompt_markdown,
load_packaged_markdown,
)

__all__ = [
"apply_global_base_prompts",
"load_agent_prompt_markdown",
"load_packaged_markdown",
]
40 changes: 40 additions & 0 deletions libs/fred-sdk/fred_sdk/resources/prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

from .packaged import load_packaged_resource

# Add one `(package, path_parts)` tuple here for each shared prompt fragment
# that should be available to authored agent pods. Fragments are appended in
# declaration order.
GLOBAL_BASE_PROMPT_RESOURCES: tuple[tuple[str, tuple[str, ...]], ...] = (
("fred_sdk", ("resources", "prompts", "mermaid_output_contract.md")),
)


def load_packaged_markdown(*, package: str, path_parts: Sequence[str]) -> str:
"""
Expand Down Expand Up @@ -57,3 +64,36 @@ def load_agent_prompt_markdown(
package=package,
path_parts=(*prompts_subdir, file_name),
)


def _join_prompt_sections(sections: Sequence[str]) -> str:
return "\n\n".join(section for section in sections if section)


GLOBAL_BASE_PROMPT_MARKDOWN: str = _join_prompt_sections(
tuple(
load_packaged_markdown(package=package, path_parts=path_parts).strip()
for package, path_parts in GLOBAL_BASE_PROMPT_RESOURCES
)
)


def apply_global_base_prompts(prompt: str) -> str:
"""
Append Fred's shared base prompt fragments to one agent prompt.

Why this helper exists:
- renderer-oriented output contracts should be reusable across agent pods
instead of copied into each application package
- prompt composition remains an authoring-time default, not a runtime
injection layer

How to use it:
- pass the agent's local system prompt and assign the returned text to the
template's default `system_prompt_template`

Example:
- `system_prompt_template = apply_global_base_prompts(local_prompt)`
"""

return _join_prompt_sections((prompt.strip(), GLOBAL_BASE_PROMPT_MARKDOWN))
Loading
Loading