mirror of
https://github.com/symfony/ai.git
synced 2026-03-23 23:42:18 +01:00
minor #1801 Redesign ai.symfony.com landing page (chr-hertel)
This PR was merged into the main branch.
Discussion
----------
Redesign ai.symfony.com landing page
| Q | A
| ------------- | ---
| Bug fix? | no
| New feature? | no
| Docs? | no
| Issues | -
| License | MIT
## Summary
Overhaul the ai.symfony.com landing page with a modern, section-based layout.
**New sections (6):**
- **Component Architecture** — layered SVG diagram with colorShift animation and component doc links
- **Features** — 10 tabbed code examples (Model Inference, Streaming, Speech, Multi-Modal, Structured Output, Agents, Token Usage, Tool Calling, Subagents, RAG) with GitHub example links
- **Third Party Integration Bridges** — 4-column logo grid with animated color-shift card backgrounds
- **Demos** — consolidated card with setup steps, copy-to-clipboard button, condensed demo grid, and GitHub repo CTA
- **MCP SDK** — official PHP SDK section with weather tool example
- **Symfony Mate** — MCP server for AI-assisted development with extension install instructions
**Other changes:**
- Hero slider with 3 rotating slides and animated progress bar
- Navbar with anchor links to page sections and smooth scrolling
- Book icon linking to documentation, GitHub icon
- Redesigned CTA section "Get Involved & Get Support"
- Updated footer links (Documentation, Releases, Support)
- Custom CSS with gradient accents, glow effects, card hover animations
- Stimulus controllers for hero slider, feature tabs, and clipboard
- Full light/dark theme support
- SEO: h1 for primary hero heading
- Accessibility: type=button on all interactive buttons, prefers-reduced-motion support, rel=noopener noreferrer on external links
- No inline styles — all styling via CSS classes
- Add CLAUDE.md for ai.symfony.com subproject
<img width="1905" height="4349" alt="localhost_8000_" src="https://github.com/user-attachments/assets/e164d3f4-0c71-457e-bbd8-4be73c6086aa" />
Commits
-------
0d492e33 Redesign ai.symfony.com landing page
This commit is contained in:
47
ai.symfony.com/CLAUDE.md
Normal file
47
ai.symfony.com/CLAUDE.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Overview
|
||||
|
||||
This is the **ai.symfony.com** marketing website — a lightweight Symfony 8.0 application serving the landing page for the Symfony AI project. It has a single controller, no database, and no complex backend logic.
|
||||
|
||||
## Development Commands
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
composer install
|
||||
|
||||
# Start development server
|
||||
symfony server:start
|
||||
|
||||
# Clear cache
|
||||
bin/console cache:clear
|
||||
|
||||
# List available asset paths
|
||||
bin/console debug:asset-map
|
||||
|
||||
# Add a new JS/CSS dependency via importmap
|
||||
bin/console importmap:require <package>
|
||||
```
|
||||
|
||||
There is no test suite for this application.
|
||||
|
||||
## Architecture
|
||||
|
||||
- **Single route**: `DefaultController::homepage()` serves `/` rendering `homepage.html.twig`
|
||||
- **Asset Mapper** (not Webpack): Frontend assets managed via Symfony's importmap system (`importmap.php`). No npm/node required.
|
||||
- **Stimulus controllers** in `assets/controllers/`: `typed_controller.js` (typing animation for hero code example), `csrf_protection_controller.js`
|
||||
- **Bootstrap 5** for layout/styling, with custom CSS variables in `assets/styles/app.css` supporting light/dark theme toggle
|
||||
- **Templates**: `templates/base.html.twig` (layout), `templates/homepage.html.twig` (page content), `templates/_header.html.twig` (navigation partial)
|
||||
|
||||
## Key Files
|
||||
|
||||
- `importmap.php` — Declares all JS/CSS dependencies and their versions (replaces package.json)
|
||||
- `assets/app.js` — Entry point; handles theme switching and initializes Stimulus
|
||||
- `assets/styles/app.css` — All custom styles including CSS variables for theming
|
||||
- `config/reference.php` — Auto-generated config reference (do not edit manually)
|
||||
|
||||
## PHP Version
|
||||
|
||||
Requires PHP 8.4+.
|
||||
@@ -1,10 +1,17 @@
|
||||
import './stimulus_bootstrap.js';
|
||||
import 'bootstrap';
|
||||
import { Tooltip } from 'bootstrap';
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import './styles/app.css';
|
||||
import AOS from 'aos';
|
||||
import 'aos/dist/aos.css';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
new App();
|
||||
AOS.init({
|
||||
duration: 700,
|
||||
once: true,
|
||||
});
|
||||
document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(el => new Tooltip(el));
|
||||
});
|
||||
|
||||
class App {
|
||||
|
||||
23
ai.symfony.com/assets/controllers/clipboard_controller.js
Normal file
23
ai.symfony.com/assets/controllers/clipboard_controller.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Controller } from '@hotwired/stimulus';
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = ['source', 'button'];
|
||||
|
||||
async copy() {
|
||||
const code = this.sourceTarget.textContent
|
||||
.replace(/^\$ /gm, '')
|
||||
.trim();
|
||||
|
||||
if (!navigator.clipboard) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(code);
|
||||
this.buttonTarget.classList.add('copied');
|
||||
setTimeout(() => this.buttonTarget.classList.remove('copied'), 2000);
|
||||
} catch {
|
||||
// Clipboard access denied — silently ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
17
ai.symfony.com/assets/controllers/feature_tabs_controller.js
Normal file
17
ai.symfony.com/assets/controllers/feature_tabs_controller.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Controller } from '@hotwired/stimulus';
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = ['tab', 'panel'];
|
||||
|
||||
switch(event) {
|
||||
const index = parseInt(event.currentTarget.dataset.index, 10);
|
||||
|
||||
this.tabTargets.forEach((tab, i) => {
|
||||
tab.classList.toggle('active', i === index);
|
||||
});
|
||||
|
||||
this.panelTargets.forEach((panel, i) => {
|
||||
panel.classList.toggle('active', i === index);
|
||||
});
|
||||
}
|
||||
}
|
||||
134
ai.symfony.com/assets/controllers/hero_slider_controller.js
Normal file
134
ai.symfony.com/assets/controllers/hero_slider_controller.js
Normal file
@@ -0,0 +1,134 @@
|
||||
import { Controller } from '@hotwired/stimulus';
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = ['slide', 'indicator', 'progress'];
|
||||
static values = {
|
||||
interval: { type: Number, default: 8000 },
|
||||
};
|
||||
|
||||
connect() {
|
||||
this.currentIndex = 0;
|
||||
this.paused = false;
|
||||
this.prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
|
||||
this.slideTargets.forEach((slide, index) => {
|
||||
if (0 !== index) {
|
||||
slide.classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
this.#scheduleNext(this.intervalValue);
|
||||
|
||||
this.handleMouseEnter = () => this.#pause();
|
||||
this.handleMouseLeave = () => this.#resume();
|
||||
this.element.addEventListener('mouseenter', this.handleMouseEnter);
|
||||
this.element.addEventListener('mouseleave', this.handleMouseLeave);
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
this.#clearTimer();
|
||||
this.element.removeEventListener('mouseenter', this.handleMouseEnter);
|
||||
this.element.removeEventListener('mouseleave', this.handleMouseLeave);
|
||||
}
|
||||
|
||||
goToSlide(event) {
|
||||
const index = parseInt(event.currentTarget.dataset.index, 10);
|
||||
|
||||
if (index === this.currentIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#clearTimer();
|
||||
this.#showSlide(index);
|
||||
this.#scheduleNext(this.intervalValue);
|
||||
}
|
||||
|
||||
#showSlide(index) {
|
||||
this.slideTargets[this.currentIndex].classList.remove('active');
|
||||
this.slideTargets[index].classList.add('active');
|
||||
|
||||
this.indicatorTargets.forEach((indicator, i) => {
|
||||
indicator.classList.toggle('active', i === index);
|
||||
});
|
||||
|
||||
this.currentIndex = index;
|
||||
}
|
||||
|
||||
#scheduleNext(delay, resetBar = true) {
|
||||
if (this.prefersReducedMotion || this.paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.remainingTime = delay;
|
||||
this.timerStart = Date.now();
|
||||
|
||||
this.#startProgressBar(delay, resetBar);
|
||||
|
||||
this.timerId = setTimeout(() => {
|
||||
this.timerId = null;
|
||||
const nextIndex = (this.currentIndex + 1) % this.slideTargets.length;
|
||||
this.#showSlide(nextIndex);
|
||||
this.#scheduleNext(this.intervalValue);
|
||||
}, delay);
|
||||
}
|
||||
|
||||
#pause() {
|
||||
this.paused = true;
|
||||
|
||||
if (this.timerId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearTimeout(this.timerId);
|
||||
this.timerId = null;
|
||||
|
||||
const elapsed = Date.now() - this.timerStart;
|
||||
this.remainingTime = Math.max(0, this.remainingTime - elapsed);
|
||||
|
||||
this.#freezeProgressBar();
|
||||
}
|
||||
|
||||
#resume() {
|
||||
this.paused = false;
|
||||
this.#scheduleNext(this.remainingTime, false);
|
||||
}
|
||||
|
||||
#clearTimer() {
|
||||
if (this.timerId) {
|
||||
clearTimeout(this.timerId);
|
||||
this.timerId = null;
|
||||
}
|
||||
|
||||
this.remainingTime = this.intervalValue;
|
||||
}
|
||||
|
||||
#startProgressBar(duration, reset = true) {
|
||||
if (!this.hasProgressTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bar = this.progressTarget;
|
||||
|
||||
if (reset) {
|
||||
bar.style.transition = 'none';
|
||||
bar.style.width = '0';
|
||||
}
|
||||
|
||||
// Force reflow so the browser registers the current width before animating
|
||||
bar.offsetWidth; // eslint-disable-line no-unused-expressions
|
||||
|
||||
bar.style.transition = 'width ' + duration + 'ms linear';
|
||||
bar.style.width = '100%';
|
||||
}
|
||||
|
||||
#freezeProgressBar() {
|
||||
if (!this.hasProgressTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bar = this.progressTarget;
|
||||
const currentWidth = getComputedStyle(bar).width;
|
||||
bar.style.transition = 'none';
|
||||
bar.style.width = currentWidth;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<svg width="285" height="81" viewBox="0 0 285 81" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M60.582 14.9102C63.4049 14.9102 65.6934 17.1987 65.6934 20.0215V60.9111C65.6934 63.734 63.4049 66.0225 60.582 66.0225H19.6924C16.8696 66.0225 14.5811 63.734 14.5811 60.9111V20.0215C14.5811 17.1987 16.8696 14.9102 19.6924 14.9102H60.582ZM53.0811 21.5352C50.2373 21.6324 47.7542 23.2024 45.9062 25.3682C43.8603 27.7467 42.4995 30.5653 41.5176 33.4443C39.7642 32.0057 38.412 30.1446 35.5967 29.335C33.4214 28.7095 31.1366 28.9664 29.0352 30.5322C28.0398 31.2754 27.3532 32.3984 27.0273 33.457C26.1829 36.2018 27.9151 38.6459 28.7021 39.5225L30.4229 41.3652C30.7767 41.7271 31.631 42.6698 31.2129 44.0205C30.763 45.4915 28.9912 46.4408 27.1738 45.8828C26.3617 45.6336 25.1957 45.0296 25.457 44.1797C25.5639 43.8308 25.8135 43.5682 25.9482 43.2705C26.07 43.0106 26.13 42.8169 26.167 42.7012C26.4987 41.6186 26.0443 40.2083 24.8838 39.8496C23.8002 39.5172 22.6916 39.7806 22.2617 41.1748C21.7747 42.759 22.5335 45.6354 26.5957 46.8867C31.3543 48.3511 35.379 45.7588 35.9502 42.3809C36.3102 40.265 35.3535 38.6916 33.6035 36.6699L32.1768 35.0908C31.3133 34.2279 31.0163 32.7565 31.9102 31.626C32.6653 30.671 33.7388 30.2652 35.5 30.7432C38.0702 31.4401 39.2151 33.2234 41.126 34.6621C40.3382 37.2505 39.8218 39.8492 39.3555 42.1787L39.0693 43.916C37.7037 51.0792 36.66 55.0148 33.9502 57.2734C33.4036 57.6624 32.6232 58.2447 31.4473 58.2861C30.8293 58.305 30.6305 57.8792 30.6221 57.6934C30.6081 57.262 30.9718 57.0632 31.2139 56.8691C31.5767 56.671 32.1243 56.3435 32.0869 55.2939C32.0474 54.0536 31.0206 52.9781 29.5352 53.0273C28.423 53.0648 26.7277 54.1106 26.792 56.0273C26.8575 58.0075 28.702 59.4907 31.4844 59.3965C32.9716 59.3466 36.2926 58.7417 39.5645 54.8525C43.3736 50.3921 44.4391 45.2809 45.2402 41.5391L46.1348 36.5977C46.6311 36.6577 47.1631 36.6985 47.7412 36.7119C52.4851 36.8122 54.8562 34.3563 54.8936 32.5684C54.9176 31.4867 54.1844 30.4208 53.1572 30.4463C52.4235 30.4669 51.5001 30.9567 51.2793 31.9717C51.0616 32.9675 52.7885 33.868 51.4395 34.7441C50.4811 35.3641 48.7627 35.8001 46.3428 35.4463L46.7822 33.0146C47.6803 28.4036 48.788 22.7317 52.9902 22.5928C53.2967 22.5784 54.4163 22.6061 54.4424 23.3477C54.4497 23.5936 54.3883 23.6589 54.0996 24.2236C53.8043 24.6649 53.6925 25.0425 53.707 25.4736C53.7475 26.6502 54.6423 27.4249 55.9385 27.3799C57.6716 27.3215 58.1691 25.635 58.1406 24.7676C58.0697 22.7291 55.9223 21.4413 53.0811 21.5352Z" fill="white"/>
|
||||
<path d="M179.412 31.8252C185.886 31.8252 190.226 36.5019 190.226 42.9726C190.226 49.0693 185.802 54.1182 179.412 54.1182C172.984 54.1182 168.561 49.0693 168.561 42.9726C168.561 36.5019 172.898 31.8252 179.412 31.8252ZM179.412 50.9502C184.005 50.9502 186.049 46.7744 186.049 42.9726C186.049 38.9248 183.586 35.001 179.412 35.001C175.196 35.001 172.732 38.9248 172.732 42.9726C172.732 46.7744 174.779 50.9502 179.412 50.9502Z" fill="white"/>
|
||||
<path d="M168.022 33.8076V32.3262H162.385V30.3017C162.385 27.4209 162.804 25.2471 166.183 25.2471C166.245 25.2471 166.31 25.2529 166.376 25.2559C166.383 25.2559 166.381 25.2451 166.389 25.2451C167.327 25.3135 168.108 24.5498 168.161 23.6123L168.229 22.3262C167.436 22.2002 166.6 22.0762 165.6 22.0762C159.797 22.0762 158.462 25.458 158.462 30.6357V32.3262H153.447V33.9795C153.576 34.8369 154.31 35.4971 155.205 35.4971C155.209 35.4971 155.214 35.501 155.219 35.501H158.462V53.6201H160.629C161.529 53.6201 162.263 52.9424 162.385 52.0791V35.501H166.31C167.233 35.4707 167.977 34.7314 168.022 33.8076Z" fill="white"/>
|
||||
<path d="M115.748 32.3262C115.742 32.3262 115.737 32.331 115.727 32.331C114.901 32.331 114.1 32.8994 113.75 33.6679L108.351 49.9463H108.267L102.985 33.6767C102.64 32.9033 101.835 32.331 101.003 32.331C100.993 32.331 100.99 32.3262 100.983 32.3262H98.2461L105.469 52.2002C105.721 52.9111 106.223 53.9971 106.223 54.4541C106.223 54.874 105.053 59.7139 101.543 59.7139C101.458 59.7139 101.369 59.7041 101.284 59.6982C100.389 59.6494 99.7134 60.2744 99.564 61.2373L99.4566 62.5967C100.167 62.7217 100.879 62.8896 102.13 62.8896C107.307 62.8896 108.851 58.1689 110.314 54.0791L118.162 32.3262H115.748Z" fill="white"/>
|
||||
<path d="M90.7991 36.6515C87.5589 34.9933 84.012 33.8663 83.9505 30.5304C83.9603 26.9874 87.2166 26.0567 89.7244 26.0597C89.7366 26.0577 89.7464 26.0577 89.7528 26.0577C90.8431 26.0577 91.7142 26.1661 92.5648 26.3526C92.576 26.3526 92.574 26.339 92.5867 26.339C93.4866 26.4054 94.242 25.7022 94.3465 24.8155L94.4183 23.5284C92.7737 23.1202 91.0545 22.9112 89.5579 22.9112C84.1028 22.9454 80.0516 25.6905 80.0389 30.9288C80.0452 35.5089 83.1307 37.2843 86.4529 38.8917C89.7093 40.4581 93.2742 41.7501 93.305 45.5304C93.2869 49.4737 89.4808 50.9268 86.8582 50.9327C85.324 50.9268 83.659 50.546 82.2557 50.1026C81.3768 49.9581 80.6561 50.7335 80.5408 51.7608L80.4232 52.9073C82.3524 53.5304 84.3294 54.0753 86.3182 54.0753H86.3402C92.4525 54.0304 97.202 51.5929 97.221 45.2647C97.2127 40.382 94.1038 38.3311 90.7991 36.6515Z" fill="white"/>
|
||||
<path d="M150.157 53.6201H150.162C151.008 53.6201 151.715 53.0244 151.893 52.2314V39.9678C151.893 35.333 149.935 31.8252 144.504 31.8252C142.584 31.8252 139.371 32.9131 137.909 35.96C136.783 33.0791 134.319 31.8252 131.858 31.8252C128.725 31.8252 126.595 32.9521 125.093 35.417H125.009V34.0928C124.998 33.1182 124.208 32.331 123.232 32.331C123.223 32.331 123.219 32.3262 123.213 32.3262H121.336V53.6201H123.464C124.449 53.6201 125.245 52.8213 125.245 51.8389C125.245 51.8115 125.253 51.8017 125.262 51.7842V42.8896C125.262 38.9248 126.847 35.001 130.854 35.001C134.025 35.001 134.656 38.2978 134.656 40.9287V53.6201H136.815C137.745 53.6201 138.494 52.9053 138.577 51.9971V42.8896C138.577 38.9248 140.165 35.001 144.171 35.001C147.344 35.001 147.971 38.2978 147.971 40.9287V53.6201H150.157Z" fill="white"/>
|
||||
<path d="M210.804 53.6202H210.808C211.765 53.6202 212.536 52.8643 212.582 51.9171V40.9698C212.582 35.2511 210.116 31.8253 204.605 31.8253C201.641 31.8253 198.805 33.2882 197.633 35.711H197.551V34.1202C197.551 34.1183 197.547 34.1183 197.547 34.1143C197.547 33.127 196.748 32.3311 195.767 32.3311C195.757 32.3311 195.755 32.3263 195.748 32.3263H193.75V53.6202H195.911C196.845 53.6202 197.602 52.8936 197.674 51.9776V43.7667C197.674 38.5909 199.68 35.0011 204.146 35.0011C207.573 35.2081 208.653 37.628 208.653 42.5977V53.6202H210.804Z" fill="white"/>
|
||||
<path d="M231.931 32.3262C231.921 32.3262 231.917 32.331 231.908 32.331C231.081 32.331 230.279 32.8994 229.931 33.6679L224.533 49.9463H224.445L219.163 33.6767C218.817 32.9033 218.013 32.331 217.183 32.331C217.175 32.331 217.17 32.3262 217.163 32.3262H214.426L221.649 52.2002C221.899 52.9111 222.404 53.9971 222.404 54.4541C222.404 54.874 221.233 59.7139 217.724 59.7139C217.639 59.7139 217.553 59.7041 217.465 59.6982C216.57 59.6494 215.896 60.2744 215.743 61.2373L215.638 62.5967C216.347 62.7217 217.06 62.8896 218.312 62.8896C223.488 62.8896 225.032 58.1689 226.496 54.0791L234.345 32.3262H231.931Z" fill="white"/>
|
||||
<path d="M253.399 47.454H240.104L238.502 52.0042C238.094 53.165 236.997 53.9416 235.766 53.9416H232.383L243.304 23.4151C243.577 22.6519 244.3 22.1426 245.11 22.1426H248.439C249.25 22.1426 249.973 22.6519 250.246 23.4151L261.167 53.9416H258.141C256.668 53.9416 255.356 53.0125 254.867 51.6237L253.399 47.454ZM251.938 43.205L246.775 28.4472L241.567 43.205H251.938Z" fill="white"/>
|
||||
<path d="M269.875 24.508V51.6226C269.875 52.9036 268.837 53.9421 267.556 53.9421H264.666V22.1885H267.556C268.837 22.1885 269.875 23.227 269.875 24.508Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 7.4 KiB |
@@ -11,6 +11,10 @@
|
||||
--sf-ai-card-border: rgba(15, 23, 42, 0.06);
|
||||
--sf-ai-logo-pill-bg: #f9fafb;
|
||||
--sf-ai-logo-pill-color: inherit;
|
||||
--sf-ai-brand-1: #5a55a5;
|
||||
--sf-ai-brand-2: #4c1d95;
|
||||
--sf-ai-brand-3: #db2777;
|
||||
--sf-ai-brand-4: #1d4ed8;
|
||||
}
|
||||
|
||||
[data-bs-theme="dark"] {
|
||||
@@ -44,6 +48,11 @@
|
||||
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
|
||||
}
|
||||
|
||||
html { scroll-behavior: smooth; scroll-padding-top: 77px; }
|
||||
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
|
||||
|
||||
.z-above { position: relative; z-index: 1; }
|
||||
|
||||
body {
|
||||
background: var(--sf-ai-page-bg);
|
||||
color: var(--sf-ai-text-color);
|
||||
@@ -65,6 +74,15 @@ a:hover {
|
||||
color: var(--sf-ai-link-color);
|
||||
text-decoration: underline;
|
||||
}
|
||||
a.btn:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
a.btn.btn-outline-secondary:hover,
|
||||
button.btn.btn-outline-secondary:hover {
|
||||
background: var(--sf-ai-bg-primary);
|
||||
color: #fff !important;
|
||||
border-color: var(--sf-ai-bg-primary);
|
||||
}
|
||||
|
||||
a.stretched-link:after {
|
||||
background: transparent;
|
||||
@@ -83,73 +101,202 @@ a.stretched-link:after {
|
||||
}
|
||||
|
||||
section.section-secondary {
|
||||
background-color: var(--sf-ai-bg-secondary);
|
||||
background: linear-gradient(180deg, oklch(95% 0.004 264.531) 0%, oklch(89% 0.008 264.531) 100%);
|
||||
}
|
||||
|
||||
[data-bs-theme="dark"] section.section-secondary {
|
||||
background: linear-gradient(180deg, oklch(22% 0.012 264.531) 0%, oklch(17% 0.018 264.531) 100%);
|
||||
}
|
||||
|
||||
.hero-wrapper {
|
||||
background-image: linear-gradient(
|
||||
position: relative;
|
||||
background-color: var(--sf-ai-brand-1);
|
||||
}
|
||||
.hero-wrapper::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
hsl(305deg 73% 25%) 0%,
|
||||
hsl(296deg 55% 29%) 1%,
|
||||
hsl(286deg 47% 33%) 2%,
|
||||
hsl(276deg 42% 37%) 4%,
|
||||
hsl(265deg 38% 42%) 7%,
|
||||
hsl(255deg 35% 45%) 10%,
|
||||
hsl(244deg 32% 49%) 15%,
|
||||
hsl(244deg 32% 44%) 21%,
|
||||
hsl(244deg 32% 38%) 29%,
|
||||
hsl(244deg 32% 33%) 40%,
|
||||
hsl(244deg 32% 28%) 57%,
|
||||
hsl(244deg 32% 23%) 79%,
|
||||
hsl(244deg 33% 19%) 100%
|
||||
rgba(255, 255, 255, 0.1) 0%,
|
||||
transparent 30%,
|
||||
rgba(0, 0, 0, 0.15) 100%
|
||||
);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes colorShift {
|
||||
0% { fill: var(--sf-ai-brand-1); background-color: var(--sf-ai-brand-1); }
|
||||
25% { fill: var(--sf-ai-brand-2); background-color: var(--sf-ai-brand-2); }
|
||||
50% { fill: var(--sf-ai-brand-3); background-color: var(--sf-ai-brand-3); }
|
||||
75% { fill: var(--sf-ai-brand-4); background-color: var(--sf-ai-brand-4); }
|
||||
100% { fill: var(--sf-ai-brand-1); background-color: var(--sf-ai-brand-1); }
|
||||
}
|
||||
|
||||
.color-shift {
|
||||
animation: colorShift 60s ease-in-out infinite;
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.color-shift, .arch-shift-strong, .arch-shift-medium, .arch-shift-light, .arch-shift-muted, .sf-ai-card-shift::before, .feature-tab.active {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Architecture layer color-shift variants */
|
||||
/* Card with color-shift background via pseudo-element */
|
||||
.sf-ai-card-shift {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.sf-ai-card-shift::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
animation: colorShift 60s ease-in-out infinite;
|
||||
opacity: 0.15;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
.sf-ai-card-shift > * { position: relative; z-index: 1; }
|
||||
|
||||
.arch-shift-strong, .arch-shift-medium, .arch-shift-light, .arch-shift-muted {
|
||||
animation: colorShift 60s ease-in-out infinite;
|
||||
}
|
||||
.arch-shift-medium { opacity: 0.55; }
|
||||
.arch-shift-light { opacity: 0.3; }
|
||||
.arch-shift-muted { opacity: 0.15; }
|
||||
|
||||
.logo-icon,
|
||||
.logo-ai { fill: var(--sf-ai-brand-1); }
|
||||
.logo-emblem { fill: #fff; }
|
||||
.logo-text { fill: var(--sf-ai-text-color); }
|
||||
|
||||
.nav-link {
|
||||
--bs-nav-link-color: #ffffff;
|
||||
--bs-nav-link-color: var(--sf-ai-text-color);
|
||||
border-radius: 0.375rem;
|
||||
transition: background-color 0.15s ease;
|
||||
}
|
||||
.nav-link:hover {
|
||||
background-color: rgba(90, 85, 165, 0.1);
|
||||
color: #5a55a5;
|
||||
text-decoration: none;
|
||||
}
|
||||
.nav-item svg {
|
||||
color: #ffffff;
|
||||
color: var(--sf-ai-text-color);
|
||||
}
|
||||
.nav-item:hover svg {
|
||||
color: #5a55a5;
|
||||
}
|
||||
button#themeToggle {
|
||||
margin: var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);
|
||||
}
|
||||
|
||||
.hero-wrapper header,
|
||||
header {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
z-index: 1030;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
[data-bs-theme="dark"] header {
|
||||
background: rgba(22, 14, 46, 0.9) !important;
|
||||
border-color: var(--sf-ai-card-border) !important;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.hero-wrapper section {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.hero-highlight {
|
||||
color: #fff;
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
padding: 0.05em 0.35em;
|
||||
border-radius: 0.25em;
|
||||
}
|
||||
|
||||
.hero-slides {
|
||||
position: relative;
|
||||
}
|
||||
.hero-slide {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.6s ease, visibility 0.6s ease;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.hero-slide.active {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
position: relative;
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.hero-slide {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-indicators {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
.hero-indicator {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid rgba(255, 255, 255, 0.5);
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease, border-color 0.3s ease;
|
||||
}
|
||||
.hero-indicator.active {
|
||||
background: #fff;
|
||||
border-color: #fff;
|
||||
}
|
||||
.hero-indicator:hover {
|
||||
border-color: #fff;
|
||||
}
|
||||
|
||||
.hero-progress {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
z-index: 2;
|
||||
}
|
||||
.hero-progress-bar {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
background: #fff;
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.hero-progress {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-section {
|
||||
padding-top: 4rem;
|
||||
padding-bottom: 4rem;
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.hero-section {
|
||||
padding-top: 6rem;
|
||||
padding-bottom: 5rem;
|
||||
padding-top: 3rem;
|
||||
padding-bottom: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.pill-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
font-size: 0.75rem;
|
||||
backdrop-filter: blur(12px);
|
||||
}
|
||||
|
||||
.pill-dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 999px;
|
||||
background-color: #34d399;
|
||||
}
|
||||
|
||||
.sf-ai-card {
|
||||
background: var(--sf-ai-card-bg);
|
||||
@@ -167,7 +314,10 @@ button#themeToggle {
|
||||
box-shadow: inset 0 0 0 2px var(--sf-ai-link-color);
|
||||
}
|
||||
.sf-ai-card-hover.sf-ai-card-hover-dark:hover {
|
||||
box-shadow: inset 0 0 0 2px var(--sf-ai-text-color);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
[data-bs-theme="dark"] .sf-ai-card-hover.sf-ai-card-hover-dark:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.sf-ai-card-body {
|
||||
@@ -190,7 +340,10 @@ button#themeToggle {
|
||||
text-align: center;
|
||||
}
|
||||
.logo-pill:hover {
|
||||
background: var(--sf-ai-bg-secondary);
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
[data-bs-theme="dark"] .logo-pill:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.logo-pill svg {
|
||||
display: block;
|
||||
@@ -214,6 +367,14 @@ button#themeToggle {
|
||||
.demo-icon svg {
|
||||
color: white;
|
||||
}
|
||||
.sf-ai-card a {
|
||||
color: var(--sf-ai-text-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
.sf-ai-card a:hover {
|
||||
color: var(--sf-ai-accent);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.demo-blog .demo-icon { background: linear-gradient(180deg, #433F77, #C43BC2); }
|
||||
.demo-crop .demo-icon { background: linear-gradient(180deg, #85A72B, #97BC43); }
|
||||
@@ -224,9 +385,63 @@ button#themeToggle {
|
||||
.demo-wikipedia .demo-icon { background: linear-gradient(180deg, #1CA574, #56AB48); }
|
||||
.demo-youtube .demo-icon { background: linear-gradient(180deg, #C05920, #CF781A); }
|
||||
|
||||
.demo-icon-sm {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 0.5rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.demo-icon-sm svg { color: white; }
|
||||
.demo-desc { font-size: 0.75rem; line-height: 1.3; display: block; }
|
||||
.demo-icon-sm.demo-blog { background: linear-gradient(180deg, #433F77, #C43BC2); }
|
||||
.demo-icon-sm.demo-crop { background: linear-gradient(180deg, #85A72B, #97BC43); }
|
||||
.demo-icon-sm.demo-recipe { background: linear-gradient(180deg, #83A659, #71BCB8); }
|
||||
.demo-icon-sm.demo-speech { background: linear-gradient(180deg, #42DEEE, #7069B0); }
|
||||
.demo-icon-sm.demo-turbo { background: linear-gradient(180deg, #E94E77, #D68189); }
|
||||
.demo-icon-sm.demo-video { background: linear-gradient(180deg, #3B9D87, #35A781); }
|
||||
.demo-icon-sm.demo-wikipedia { background: linear-gradient(180deg, #1CA574, #56AB48); }
|
||||
.demo-icon-sm.demo-youtube { background: linear-gradient(180deg, #C05920, #CF781A); }
|
||||
|
||||
.cta-section {
|
||||
background-color: var(--sf-ai-brand-1);
|
||||
}
|
||||
.cta-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(255, 255, 255, 0.08) 0%,
|
||||
transparent 50%,
|
||||
rgba(0, 0, 0, 0.12) 100%
|
||||
);
|
||||
pointer-events: none;
|
||||
}
|
||||
.cta-card {
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
border-radius: var(--sf-ai-card-radius);
|
||||
backdrop-filter: blur(8px);
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
transition: background 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
.cta-card:hover {
|
||||
background: rgba(255, 255, 255, 0.14);
|
||||
transform: translateY(-2px);
|
||||
color: #fff;
|
||||
}
|
||||
.cta-card svg {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.footer-meta {
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.8;
|
||||
color: var(--sf-ai-text-color);
|
||||
}
|
||||
.footer-meta svg {
|
||||
color: var(--sf-ai-text-color);
|
||||
}
|
||||
|
||||
.terminal {
|
||||
@@ -235,7 +450,36 @@ button#themeToggle {
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
overflow: hidden;
|
||||
padding: 15px 20px;
|
||||
position: relative;
|
||||
box-shadow: 0 8px 32px rgba(127, 91, 255, 0.08), 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
[data-bs-theme="dark"] .terminal {
|
||||
box-shadow: 0 8px 40px rgba(127, 91, 255, 0.15), 0 2px 8px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
/* Copy button */
|
||||
.copy-btn {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 24px;
|
||||
z-index: 2;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
border-radius: 0.5rem;
|
||||
padding: 6px 8px;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
cursor: pointer;
|
||||
transition: color 0.15s ease, background 0.15s ease;
|
||||
line-height: 1;
|
||||
}
|
||||
.copy-btn:hover {
|
||||
color: #fff;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
.copy-btn.copied {
|
||||
color: #34d399;
|
||||
}
|
||||
|
||||
.terminal code {
|
||||
font-family: JetBrainsMono, ui-monospace;
|
||||
font-optical-sizing: auto;
|
||||
@@ -244,9 +488,162 @@ button#themeToggle {
|
||||
font-synthesis: none;
|
||||
font-variant-ligatures: none;
|
||||
}
|
||||
code { color: oklch(87% 0 0); }
|
||||
code .variable { color: #79c0ff; }
|
||||
code .title, code .title.class { color: #d2a8ff; }
|
||||
code .keyword { color: #ff7b72; }
|
||||
code .string { color: #9c9; }
|
||||
.terminal code { color: oklch(87% 0 0); }
|
||||
.terminal code .variable { color: #79c0ff; }
|
||||
.terminal code .title, .terminal code .title.class { color: #d2a8ff; }
|
||||
.terminal code .keyword { color: #ff7b72; }
|
||||
.terminal code .string { color: #9c9; }
|
||||
.terminal code .comment { color: #8b949e; }
|
||||
|
||||
/* Section headings */
|
||||
.section-heading {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding-bottom: 0.75rem;
|
||||
}
|
||||
.section-heading::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 60px;
|
||||
height: 3px;
|
||||
border-radius: 3px;
|
||||
background: linear-gradient(90deg, var(--sf-ai-brand-1), var(--sf-ai-accent), var(--sf-ai-brand-3));
|
||||
}
|
||||
|
||||
|
||||
/* Architecture */
|
||||
.architecture-diagram { max-width: 680px; margin-left: auto; }
|
||||
.architecture-diagram svg {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
filter: drop-shadow(0 4px 20px rgba(127, 91, 255, 0.1));
|
||||
transition: filter 0.3s ease;
|
||||
}
|
||||
.architecture-diagram:hover svg {
|
||||
filter: drop-shadow(0 8px 32px rgba(127, 91, 255, 0.2));
|
||||
}
|
||||
.component-link {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 0.75rem;
|
||||
border-left: 3px solid transparent;
|
||||
text-decoration: none;
|
||||
color: var(--sf-ai-text-color);
|
||||
transition: background-color 0.15s ease, border-color 0.2s ease, transform 0.15s ease;
|
||||
}
|
||||
.component-link:hover {
|
||||
background: var(--sf-ai-bg-secondary);
|
||||
text-decoration: none;
|
||||
color: var(--sf-ai-text-color);
|
||||
transform: translateX(4px);
|
||||
}
|
||||
.component-link:nth-child(1):hover { border-left-color: var(--sf-ai-brand-4); }
|
||||
.component-link:nth-child(2):hover { border-left-color: var(--sf-ai-brand-3); }
|
||||
.component-link:nth-child(3):hover { border-left-color: var(--sf-ai-brand-2); }
|
||||
.component-link:nth-child(4):hover { border-left-color: var(--sf-ai-brand-1); }
|
||||
.component-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
margin-top: 0.5em;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 0 6px currentColor;
|
||||
}
|
||||
.component-dot-platform { background: var(--sf-ai-brand-4); }
|
||||
.component-dot-store { background: var(--sf-ai-brand-3); }
|
||||
.component-dot-agent { background: var(--sf-ai-brand-2); }
|
||||
.component-dot-bundle { background: var(--sf-ai-brand-1); }
|
||||
|
||||
/* Feature tabs */
|
||||
.feature-tab {
|
||||
border: none;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.55rem 1.25rem;
|
||||
background: var(--sf-ai-card-bg);
|
||||
color: var(--sf-ai-text-color);
|
||||
font-family: var(--font-family-title);
|
||||
font-weight: 600;
|
||||
font-size: 0.875rem;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
.feature-tab:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
.feature-tab.active {
|
||||
animation: colorShift 60s ease-in-out infinite;
|
||||
color: #fff;
|
||||
box-shadow: 0 4px 20px rgba(90, 85, 165, 0.4);
|
||||
}
|
||||
.feature-panel {
|
||||
display: none;
|
||||
animation: featureFadeIn 0.35s ease;
|
||||
}
|
||||
.feature-panel.active { display: block; }
|
||||
@keyframes featureFadeIn {
|
||||
from { opacity: 0; transform: translateY(8px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* Gradient hover border for cards */
|
||||
.sf-ai-card-glow {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: box-shadow 0.25s ease, transform 0.25s ease;
|
||||
}
|
||||
.sf-ai-card-glow:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 24px rgba(127, 91, 255, 0.1);
|
||||
}
|
||||
[data-bs-theme="dark"] .sf-ai-card-glow:hover {
|
||||
box-shadow: 0 8px 32px rgba(127, 91, 255, 0.18);
|
||||
}
|
||||
|
||||
/* Section decorative backgrounds */
|
||||
.section-gradient-mesh {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.section-gradient-mesh::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
right: -20%;
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(127, 91, 255, 0.06) 0%, transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.section-gradient-mesh::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: -30%;
|
||||
left: -15%;
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(219, 39, 119, 0.04) 0%, transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
[data-bs-theme="dark"] .section-gradient-mesh::before {
|
||||
background: radial-gradient(circle, rgba(127, 91, 255, 0.1) 0%, transparent 70%);
|
||||
}
|
||||
[data-bs-theme="dark"] .section-gradient-mesh::after {
|
||||
background: radial-gradient(circle, rgba(219, 39, 119, 0.07) 0%, transparent 70%);
|
||||
}
|
||||
|
||||
/* Section divider */
|
||||
.section-divider {
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, transparent, var(--sf-ai-card-border), var(--sf-ai-accent), var(--sf-ai-card-border), transparent);
|
||||
border: none;
|
||||
margin: 0;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,17 +6,17 @@
|
||||
"php": ">=8.4",
|
||||
"ext-ctype": "*",
|
||||
"ext-iconv": "*",
|
||||
"symfony/asset": "^7.4",
|
||||
"symfony/asset-mapper": "^7.4",
|
||||
"symfony/console": "^7.4",
|
||||
"symfony/dotenv": "^7.4",
|
||||
"symfony/asset": "^8.0",
|
||||
"symfony/asset-mapper": "^8.0",
|
||||
"symfony/console": "^8.0",
|
||||
"symfony/dotenv": "^8.0",
|
||||
"symfony/flex": "^2",
|
||||
"symfony/framework-bundle": "^7.4",
|
||||
"symfony/runtime": "^7.4",
|
||||
"symfony/framework-bundle": "^8.0",
|
||||
"symfony/runtime": "^8.0",
|
||||
"symfony/stimulus-bundle": "^2.31",
|
||||
"symfony/twig-bundle": "^7.4",
|
||||
"symfony/twig-bundle": "^8.0",
|
||||
"symfony/ux-icons": "^2.31",
|
||||
"symfony/yaml": "^7.4",
|
||||
"symfony/yaml": "^8.0",
|
||||
"twig/extra-bundle": "^3.0",
|
||||
"twig/twig": "^3.0"
|
||||
},
|
||||
@@ -61,7 +61,7 @@
|
||||
"extra": {
|
||||
"symfony": {
|
||||
"allow-contrib": false,
|
||||
"require": "7.4.*"
|
||||
"require": "8.0.*"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
3790
ai.symfony.com/composer.lock
generated
Normal file
3790
ai.symfony.com/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
932
ai.symfony.com/config/reference.php
Normal file
932
ai.symfony.com/config/reference.php
Normal file
@@ -0,0 +1,932 @@
|
||||
<?php
|
||||
|
||||
// This file is auto-generated and is for apps only. Bundles SHOULD NOT rely on its content.
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
|
||||
|
||||
use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
||||
|
||||
/**
|
||||
* This class provides array-shapes for configuring the services and bundles of an application.
|
||||
*
|
||||
* Services declared with the config() method below are autowired and autoconfigured by default.
|
||||
*
|
||||
* This is for apps only. Bundles SHOULD NOT use it.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```php
|
||||
* // config/services.php
|
||||
* namespace Symfony\Component\DependencyInjection\Loader\Configurator;
|
||||
*
|
||||
* return App::config([
|
||||
* 'services' => [
|
||||
* 'App\\' => [
|
||||
* 'resource' => '../src/',
|
||||
* ],
|
||||
* ],
|
||||
* ]);
|
||||
* ```
|
||||
*
|
||||
* @psalm-type ImportsConfig = list<string|array{
|
||||
* resource: string,
|
||||
* type?: string|null,
|
||||
* ignore_errors?: bool,
|
||||
* }>
|
||||
* @psalm-type ParametersConfig = array<string, scalar|\UnitEnum|array<scalar|\UnitEnum|array<mixed>|Param|null>|Param|null>
|
||||
* @psalm-type ArgumentsType = list<mixed>|array<string, mixed>
|
||||
* @psalm-type CallType = array<string, ArgumentsType>|array{0:string, 1?:ArgumentsType, 2?:bool}|array{method:string, arguments?:ArgumentsType, returns_clone?:bool}
|
||||
* @psalm-type TagsType = list<string|array<string, array<string, mixed>>> // arrays inside the list must have only one element, with the tag name as the key
|
||||
* @psalm-type CallbackType = string|array{0:string|ReferenceConfigurator,1:string}|\Closure|ReferenceConfigurator
|
||||
* @psalm-type DeprecationType = array{package: string, version: string, message?: string}
|
||||
* @psalm-type DefaultsType = array{
|
||||
* public?: bool,
|
||||
* tags?: TagsType,
|
||||
* resource_tags?: TagsType,
|
||||
* autowire?: bool,
|
||||
* autoconfigure?: bool,
|
||||
* bind?: array<string, mixed>,
|
||||
* }
|
||||
* @psalm-type InstanceofType = array{
|
||||
* shared?: bool,
|
||||
* lazy?: bool|string,
|
||||
* public?: bool,
|
||||
* properties?: array<string, mixed>,
|
||||
* configurator?: CallbackType,
|
||||
* calls?: list<CallType>,
|
||||
* tags?: TagsType,
|
||||
* resource_tags?: TagsType,
|
||||
* autowire?: bool,
|
||||
* bind?: array<string, mixed>,
|
||||
* constructor?: string,
|
||||
* }
|
||||
* @psalm-type DefinitionType = array{
|
||||
* class?: string,
|
||||
* file?: string,
|
||||
* parent?: string,
|
||||
* shared?: bool,
|
||||
* synthetic?: bool,
|
||||
* lazy?: bool|string,
|
||||
* public?: bool,
|
||||
* abstract?: bool,
|
||||
* deprecated?: DeprecationType,
|
||||
* factory?: CallbackType,
|
||||
* configurator?: CallbackType,
|
||||
* arguments?: ArgumentsType,
|
||||
* properties?: array<string, mixed>,
|
||||
* calls?: list<CallType>,
|
||||
* tags?: TagsType,
|
||||
* resource_tags?: TagsType,
|
||||
* decorates?: string,
|
||||
* decoration_inner_name?: string,
|
||||
* decoration_priority?: int,
|
||||
* decoration_on_invalid?: 'exception'|'ignore'|null,
|
||||
* autowire?: bool,
|
||||
* autoconfigure?: bool,
|
||||
* bind?: array<string, mixed>,
|
||||
* constructor?: string,
|
||||
* from_callable?: CallbackType,
|
||||
* }
|
||||
* @psalm-type AliasType = string|array{
|
||||
* alias: string,
|
||||
* public?: bool,
|
||||
* deprecated?: DeprecationType,
|
||||
* }
|
||||
* @psalm-type PrototypeType = array{
|
||||
* resource: string,
|
||||
* namespace?: string,
|
||||
* exclude?: string|list<string>,
|
||||
* parent?: string,
|
||||
* shared?: bool,
|
||||
* lazy?: bool|string,
|
||||
* public?: bool,
|
||||
* abstract?: bool,
|
||||
* deprecated?: DeprecationType,
|
||||
* factory?: CallbackType,
|
||||
* arguments?: ArgumentsType,
|
||||
* properties?: array<string, mixed>,
|
||||
* configurator?: CallbackType,
|
||||
* calls?: list<CallType>,
|
||||
* tags?: TagsType,
|
||||
* resource_tags?: TagsType,
|
||||
* autowire?: bool,
|
||||
* autoconfigure?: bool,
|
||||
* bind?: array<string, mixed>,
|
||||
* constructor?: string,
|
||||
* }
|
||||
* @psalm-type StackType = array{
|
||||
* stack: list<DefinitionType|AliasType|PrototypeType|array<class-string, ArgumentsType|null>>,
|
||||
* public?: bool,
|
||||
* deprecated?: DeprecationType,
|
||||
* }
|
||||
* @psalm-type ServicesConfig = array{
|
||||
* _defaults?: DefaultsType,
|
||||
* _instanceof?: InstanceofType,
|
||||
* ...<string, DefinitionType|AliasType|PrototypeType|StackType|ArgumentsType|null>
|
||||
* }
|
||||
* @psalm-type ExtensionType = array<string, mixed>
|
||||
* @psalm-type FrameworkConfig = array{
|
||||
* secret?: scalar|Param|null,
|
||||
* http_method_override?: bool|Param, // Set true to enable support for the '_method' request parameter to determine the intended HTTP method on POST requests. // Default: false
|
||||
* allowed_http_method_override?: list<string|Param>|null,
|
||||
* trust_x_sendfile_type_header?: scalar|Param|null, // Set true to enable support for xsendfile in binary file responses. // Default: "%env(bool:default::SYMFONY_TRUST_X_SENDFILE_TYPE_HEADER)%"
|
||||
* ide?: scalar|Param|null, // Default: "%env(default::SYMFONY_IDE)%"
|
||||
* test?: bool|Param,
|
||||
* default_locale?: scalar|Param|null, // Default: "en"
|
||||
* set_locale_from_accept_language?: bool|Param, // Whether to use the Accept-Language HTTP header to set the Request locale (only when the "_locale" request attribute is not passed). // Default: false
|
||||
* set_content_language_from_locale?: bool|Param, // Whether to set the Content-Language HTTP header on the Response using the Request locale. // Default: false
|
||||
* enabled_locales?: list<scalar|Param|null>,
|
||||
* trusted_hosts?: list<scalar|Param|null>,
|
||||
* trusted_proxies?: mixed, // Default: ["%env(default::SYMFONY_TRUSTED_PROXIES)%"]
|
||||
* trusted_headers?: list<scalar|Param|null>,
|
||||
* error_controller?: scalar|Param|null, // Default: "error_controller"
|
||||
* handle_all_throwables?: bool|Param, // HttpKernel will handle all kinds of \Throwable. // Default: true
|
||||
* csrf_protection?: bool|array{
|
||||
* enabled?: scalar|Param|null, // Default: null
|
||||
* stateless_token_ids?: list<scalar|Param|null>,
|
||||
* check_header?: scalar|Param|null, // Whether to check the CSRF token in a header in addition to a cookie when using stateless protection. // Default: false
|
||||
* cookie_name?: scalar|Param|null, // The name of the cookie to use when using stateless protection. // Default: "csrf-token"
|
||||
* },
|
||||
* form?: bool|array{ // Form configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* csrf_protection?: bool|array{
|
||||
* enabled?: scalar|Param|null, // Default: null
|
||||
* token_id?: scalar|Param|null, // Default: null
|
||||
* field_name?: scalar|Param|null, // Default: "_token"
|
||||
* field_attr?: array<string, scalar|Param|null>,
|
||||
* },
|
||||
* },
|
||||
* http_cache?: bool|array{ // HTTP cache configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* debug?: bool|Param, // Default: "%kernel.debug%"
|
||||
* trace_level?: "none"|"short"|"full"|Param,
|
||||
* trace_header?: scalar|Param|null,
|
||||
* default_ttl?: int|Param,
|
||||
* private_headers?: list<scalar|Param|null>,
|
||||
* skip_response_headers?: list<scalar|Param|null>,
|
||||
* allow_reload?: bool|Param,
|
||||
* allow_revalidate?: bool|Param,
|
||||
* stale_while_revalidate?: int|Param,
|
||||
* stale_if_error?: int|Param,
|
||||
* terminate_on_cache_hit?: bool|Param,
|
||||
* },
|
||||
* esi?: bool|array{ // ESI configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* },
|
||||
* ssi?: bool|array{ // SSI configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* },
|
||||
* fragments?: bool|array{ // Fragments configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* hinclude_default_template?: scalar|Param|null, // Default: null
|
||||
* path?: scalar|Param|null, // Default: "/_fragment"
|
||||
* },
|
||||
* profiler?: bool|array{ // Profiler configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* collect?: bool|Param, // Default: true
|
||||
* collect_parameter?: scalar|Param|null, // The name of the parameter to use to enable or disable collection on a per request basis. // Default: null
|
||||
* only_exceptions?: bool|Param, // Default: false
|
||||
* only_main_requests?: bool|Param, // Default: false
|
||||
* dsn?: scalar|Param|null, // Default: "file:%kernel.cache_dir%/profiler"
|
||||
* collect_serializer_data?: true|Param, // Default: true
|
||||
* },
|
||||
* workflows?: bool|array{
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* workflows?: array<string, array{ // Default: []
|
||||
* audit_trail?: bool|array{
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* },
|
||||
* type?: "workflow"|"state_machine"|Param, // Default: "state_machine"
|
||||
* marking_store?: array{
|
||||
* type?: "method"|Param,
|
||||
* property?: scalar|Param|null,
|
||||
* service?: scalar|Param|null,
|
||||
* },
|
||||
* supports?: list<scalar|Param|null>,
|
||||
* definition_validators?: list<scalar|Param|null>,
|
||||
* support_strategy?: scalar|Param|null,
|
||||
* initial_marking?: list<scalar|Param|null>,
|
||||
* events_to_dispatch?: list<string|Param>|null,
|
||||
* places?: list<array{ // Default: []
|
||||
* name?: scalar|Param|null,
|
||||
* metadata?: array<string, mixed>,
|
||||
* }>,
|
||||
* transitions?: list<array{ // Default: []
|
||||
* name?: string|Param,
|
||||
* guard?: string|Param, // An expression to block the transition.
|
||||
* from?: list<array{ // Default: []
|
||||
* place?: string|Param,
|
||||
* weight?: int|Param, // Default: 1
|
||||
* }>,
|
||||
* to?: list<array{ // Default: []
|
||||
* place?: string|Param,
|
||||
* weight?: int|Param, // Default: 1
|
||||
* }>,
|
||||
* weight?: int|Param, // Default: 1
|
||||
* metadata?: array<string, mixed>,
|
||||
* }>,
|
||||
* metadata?: array<string, mixed>,
|
||||
* }>,
|
||||
* },
|
||||
* router?: bool|array{ // Router configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* resource?: scalar|Param|null,
|
||||
* type?: scalar|Param|null,
|
||||
* default_uri?: scalar|Param|null, // The default URI used to generate URLs in a non-HTTP context. // Default: null
|
||||
* http_port?: scalar|Param|null, // Default: 80
|
||||
* https_port?: scalar|Param|null, // Default: 443
|
||||
* strict_requirements?: scalar|Param|null, // set to true to throw an exception when a parameter does not match the requirements set to false to disable exceptions when a parameter does not match the requirements (and return null instead) set to null to disable parameter checks against requirements 'true' is the preferred configuration in development mode, while 'false' or 'null' might be preferred in production // Default: true
|
||||
* utf8?: bool|Param, // Default: true
|
||||
* },
|
||||
* session?: bool|array{ // Session configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* storage_factory_id?: scalar|Param|null, // Default: "session.storage.factory.native"
|
||||
* handler_id?: scalar|Param|null, // Defaults to using the native session handler, or to the native *file* session handler if "save_path" is not null.
|
||||
* name?: scalar|Param|null,
|
||||
* cookie_lifetime?: scalar|Param|null,
|
||||
* cookie_path?: scalar|Param|null,
|
||||
* cookie_domain?: scalar|Param|null,
|
||||
* cookie_secure?: true|false|"auto"|Param, // Default: "auto"
|
||||
* cookie_httponly?: bool|Param, // Default: true
|
||||
* cookie_samesite?: null|"lax"|"strict"|"none"|Param, // Default: "lax"
|
||||
* use_cookies?: bool|Param,
|
||||
* gc_divisor?: scalar|Param|null,
|
||||
* gc_probability?: scalar|Param|null,
|
||||
* gc_maxlifetime?: scalar|Param|null,
|
||||
* save_path?: scalar|Param|null, // Defaults to "%kernel.cache_dir%/sessions" if the "handler_id" option is not null.
|
||||
* metadata_update_threshold?: int|Param, // Seconds to wait between 2 session metadata updates. // Default: 0
|
||||
* },
|
||||
* request?: bool|array{ // Request configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* formats?: array<string, string|list<scalar|Param|null>>,
|
||||
* },
|
||||
* assets?: bool|array{ // Assets configuration
|
||||
* enabled?: bool|Param, // Default: true
|
||||
* strict_mode?: bool|Param, // Throw an exception if an entry is missing from the manifest.json. // Default: false
|
||||
* version_strategy?: scalar|Param|null, // Default: null
|
||||
* version?: scalar|Param|null, // Default: null
|
||||
* version_format?: scalar|Param|null, // Default: "%%s?%%s"
|
||||
* json_manifest_path?: scalar|Param|null, // Default: null
|
||||
* base_path?: scalar|Param|null, // Default: ""
|
||||
* base_urls?: list<scalar|Param|null>,
|
||||
* packages?: array<string, array{ // Default: []
|
||||
* strict_mode?: bool|Param, // Throw an exception if an entry is missing from the manifest.json. // Default: false
|
||||
* version_strategy?: scalar|Param|null, // Default: null
|
||||
* version?: scalar|Param|null,
|
||||
* version_format?: scalar|Param|null, // Default: null
|
||||
* json_manifest_path?: scalar|Param|null, // Default: null
|
||||
* base_path?: scalar|Param|null, // Default: ""
|
||||
* base_urls?: list<scalar|Param|null>,
|
||||
* }>,
|
||||
* },
|
||||
* asset_mapper?: bool|array{ // Asset Mapper configuration
|
||||
* enabled?: bool|Param, // Default: true
|
||||
* paths?: array<string, scalar|Param|null>,
|
||||
* excluded_patterns?: list<scalar|Param|null>,
|
||||
* exclude_dotfiles?: bool|Param, // If true, any files starting with "." will be excluded from the asset mapper. // Default: true
|
||||
* server?: bool|Param, // If true, a "dev server" will return the assets from the public directory (true in "debug" mode only by default). // Default: true
|
||||
* public_prefix?: scalar|Param|null, // The public path where the assets will be written to (and served from when "server" is true). // Default: "/assets/"
|
||||
* missing_import_mode?: "strict"|"warn"|"ignore"|Param, // Behavior if an asset cannot be found when imported from JavaScript or CSS files - e.g. "import './non-existent.js'". "strict" means an exception is thrown, "warn" means a warning is logged, "ignore" means the import is left as-is. // Default: "warn"
|
||||
* extensions?: array<string, scalar|Param|null>,
|
||||
* importmap_path?: scalar|Param|null, // The path of the importmap.php file. // Default: "%kernel.project_dir%/importmap.php"
|
||||
* importmap_polyfill?: scalar|Param|null, // The importmap name that will be used to load the polyfill. Set to false to disable. // Default: "es-module-shims"
|
||||
* importmap_script_attributes?: array<string, scalar|Param|null>,
|
||||
* vendor_dir?: scalar|Param|null, // The directory to store JavaScript vendors. // Default: "%kernel.project_dir%/assets/vendor"
|
||||
* precompress?: bool|array{ // Precompress assets with Brotli, Zstandard and gzip.
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* formats?: list<scalar|Param|null>,
|
||||
* extensions?: list<scalar|Param|null>,
|
||||
* },
|
||||
* },
|
||||
* translator?: bool|array{ // Translator configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* fallbacks?: list<scalar|Param|null>,
|
||||
* logging?: bool|Param, // Default: false
|
||||
* formatter?: scalar|Param|null, // Default: "translator.formatter.default"
|
||||
* cache_dir?: scalar|Param|null, // Default: "%kernel.cache_dir%/translations"
|
||||
* default_path?: scalar|Param|null, // The default path used to load translations. // Default: "%kernel.project_dir%/translations"
|
||||
* paths?: list<scalar|Param|null>,
|
||||
* pseudo_localization?: bool|array{
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* accents?: bool|Param, // Default: true
|
||||
* expansion_factor?: float|Param, // Default: 1.0
|
||||
* brackets?: bool|Param, // Default: true
|
||||
* parse_html?: bool|Param, // Default: false
|
||||
* localizable_html_attributes?: list<scalar|Param|null>,
|
||||
* },
|
||||
* providers?: array<string, array{ // Default: []
|
||||
* dsn?: scalar|Param|null,
|
||||
* domains?: list<scalar|Param|null>,
|
||||
* locales?: list<scalar|Param|null>,
|
||||
* }>,
|
||||
* globals?: array<string, string|array{ // Default: []
|
||||
* value?: mixed,
|
||||
* message?: string|Param,
|
||||
* parameters?: array<string, scalar|Param|null>,
|
||||
* domain?: string|Param,
|
||||
* }>,
|
||||
* },
|
||||
* validation?: bool|array{ // Validation configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* enable_attributes?: bool|Param, // Default: true
|
||||
* static_method?: list<scalar|Param|null>,
|
||||
* translation_domain?: scalar|Param|null, // Default: "validators"
|
||||
* email_validation_mode?: "html5"|"html5-allow-no-tld"|"strict"|Param, // Default: "html5"
|
||||
* mapping?: array{
|
||||
* paths?: list<scalar|Param|null>,
|
||||
* },
|
||||
* not_compromised_password?: bool|array{
|
||||
* enabled?: bool|Param, // When disabled, compromised passwords will be accepted as valid. // Default: true
|
||||
* endpoint?: scalar|Param|null, // API endpoint for the NotCompromisedPassword Validator. // Default: null
|
||||
* },
|
||||
* disable_translation?: bool|Param, // Default: false
|
||||
* auto_mapping?: array<string, array{ // Default: []
|
||||
* services?: list<scalar|Param|null>,
|
||||
* }>,
|
||||
* },
|
||||
* serializer?: bool|array{ // Serializer configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* enable_attributes?: bool|Param, // Default: true
|
||||
* name_converter?: scalar|Param|null,
|
||||
* circular_reference_handler?: scalar|Param|null,
|
||||
* max_depth_handler?: scalar|Param|null,
|
||||
* mapping?: array{
|
||||
* paths?: list<scalar|Param|null>,
|
||||
* },
|
||||
* default_context?: array<string, mixed>,
|
||||
* named_serializers?: array<string, array{ // Default: []
|
||||
* name_converter?: scalar|Param|null,
|
||||
* default_context?: array<string, mixed>,
|
||||
* include_built_in_normalizers?: bool|Param, // Whether to include the built-in normalizers // Default: true
|
||||
* include_built_in_encoders?: bool|Param, // Whether to include the built-in encoders // Default: true
|
||||
* }>,
|
||||
* },
|
||||
* property_access?: bool|array{ // Property access configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* magic_call?: bool|Param, // Default: false
|
||||
* magic_get?: bool|Param, // Default: true
|
||||
* magic_set?: bool|Param, // Default: true
|
||||
* throw_exception_on_invalid_index?: bool|Param, // Default: false
|
||||
* throw_exception_on_invalid_property_path?: bool|Param, // Default: true
|
||||
* },
|
||||
* type_info?: bool|array{ // Type info configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* aliases?: array<string, scalar|Param|null>,
|
||||
* },
|
||||
* property_info?: bool|array{ // Property info configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* with_constructor_extractor?: bool|Param, // Registers the constructor extractor. // Default: true
|
||||
* },
|
||||
* cache?: array{ // Cache configuration
|
||||
* prefix_seed?: scalar|Param|null, // Used to namespace cache keys when using several apps with the same shared backend. // Default: "_%kernel.project_dir%.%kernel.container_class%"
|
||||
* app?: scalar|Param|null, // App related cache pools configuration. // Default: "cache.adapter.filesystem"
|
||||
* system?: scalar|Param|null, // System related cache pools configuration. // Default: "cache.adapter.system"
|
||||
* directory?: scalar|Param|null, // Default: "%kernel.share_dir%/pools/app"
|
||||
* default_psr6_provider?: scalar|Param|null,
|
||||
* default_redis_provider?: scalar|Param|null, // Default: "redis://localhost"
|
||||
* default_valkey_provider?: scalar|Param|null, // Default: "valkey://localhost"
|
||||
* default_memcached_provider?: scalar|Param|null, // Default: "memcached://localhost"
|
||||
* default_doctrine_dbal_provider?: scalar|Param|null, // Default: "database_connection"
|
||||
* default_pdo_provider?: scalar|Param|null, // Default: null
|
||||
* pools?: array<string, array{ // Default: []
|
||||
* adapters?: list<scalar|Param|null>,
|
||||
* tags?: scalar|Param|null, // Default: null
|
||||
* public?: bool|Param, // Default: false
|
||||
* default_lifetime?: scalar|Param|null, // Default lifetime of the pool.
|
||||
* provider?: scalar|Param|null, // Overwrite the setting from the default provider for this adapter.
|
||||
* early_expiration_message_bus?: scalar|Param|null,
|
||||
* clearer?: scalar|Param|null,
|
||||
* }>,
|
||||
* },
|
||||
* php_errors?: array{ // PHP errors handling configuration
|
||||
* log?: mixed, // Use the application logger instead of the PHP logger for logging PHP errors. // Default: true
|
||||
* throw?: bool|Param, // Throw PHP errors as \ErrorException instances. // Default: true
|
||||
* },
|
||||
* exceptions?: array<string, array{ // Default: []
|
||||
* log_level?: scalar|Param|null, // The level of log message. Null to let Symfony decide. // Default: null
|
||||
* status_code?: scalar|Param|null, // The status code of the response. Null or 0 to let Symfony decide. // Default: null
|
||||
* log_channel?: scalar|Param|null, // The channel of log message. Null to let Symfony decide. // Default: null
|
||||
* }>,
|
||||
* web_link?: bool|array{ // Web links configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* },
|
||||
* lock?: bool|string|array{ // Lock configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* resources?: array<string, string|list<scalar|Param|null>>,
|
||||
* },
|
||||
* semaphore?: bool|string|array{ // Semaphore configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* resources?: array<string, scalar|Param|null>,
|
||||
* },
|
||||
* messenger?: bool|array{ // Messenger configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* routing?: array<string, string|array{ // Default: []
|
||||
* senders?: list<scalar|Param|null>,
|
||||
* }>,
|
||||
* serializer?: array{
|
||||
* default_serializer?: scalar|Param|null, // Service id to use as the default serializer for the transports. // Default: "messenger.transport.native_php_serializer"
|
||||
* symfony_serializer?: array{
|
||||
* format?: scalar|Param|null, // Serialization format for the messenger.transport.symfony_serializer service (which is not the serializer used by default). // Default: "json"
|
||||
* context?: array<string, mixed>,
|
||||
* },
|
||||
* },
|
||||
* transports?: array<string, string|array{ // Default: []
|
||||
* dsn?: scalar|Param|null,
|
||||
* serializer?: scalar|Param|null, // Service id of a custom serializer to use. // Default: null
|
||||
* options?: array<string, mixed>,
|
||||
* failure_transport?: scalar|Param|null, // Transport name to send failed messages to (after all retries have failed). // Default: null
|
||||
* retry_strategy?: string|array{
|
||||
* service?: scalar|Param|null, // Service id to override the retry strategy entirely. // Default: null
|
||||
* max_retries?: int|Param, // Default: 3
|
||||
* delay?: int|Param, // Time in ms to delay (or the initial value when multiplier is used). // Default: 1000
|
||||
* multiplier?: float|Param, // If greater than 1, delay will grow exponentially for each retry: this delay = (delay * (multiple ^ retries)). // Default: 2
|
||||
* max_delay?: int|Param, // Max time in ms that a retry should ever be delayed (0 = infinite). // Default: 0
|
||||
* jitter?: float|Param, // Randomness to apply to the delay (between 0 and 1). // Default: 0.1
|
||||
* },
|
||||
* rate_limiter?: scalar|Param|null, // Rate limiter name to use when processing messages. // Default: null
|
||||
* }>,
|
||||
* failure_transport?: scalar|Param|null, // Transport name to send failed messages to (after all retries have failed). // Default: null
|
||||
* stop_worker_on_signals?: list<scalar|Param|null>,
|
||||
* default_bus?: scalar|Param|null, // Default: null
|
||||
* buses?: array<string, array{ // Default: {"messenger.bus.default":{"default_middleware":{"enabled":true,"allow_no_handlers":false,"allow_no_senders":true},"middleware":[]}}
|
||||
* default_middleware?: bool|string|array{
|
||||
* enabled?: bool|Param, // Default: true
|
||||
* allow_no_handlers?: bool|Param, // Default: false
|
||||
* allow_no_senders?: bool|Param, // Default: true
|
||||
* },
|
||||
* middleware?: list<string|array{ // Default: []
|
||||
* id?: scalar|Param|null,
|
||||
* arguments?: list<mixed>,
|
||||
* }>,
|
||||
* }>,
|
||||
* },
|
||||
* scheduler?: bool|array{ // Scheduler configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* },
|
||||
* disallow_search_engine_index?: bool|Param, // Enabled by default when debug is enabled. // Default: true
|
||||
* http_client?: bool|array{ // HTTP Client configuration
|
||||
* enabled?: bool|Param, // Default: true
|
||||
* max_host_connections?: int|Param, // The maximum number of connections to a single host.
|
||||
* default_options?: array{
|
||||
* headers?: array<string, mixed>,
|
||||
* vars?: array<string, mixed>,
|
||||
* max_redirects?: int|Param, // The maximum number of redirects to follow.
|
||||
* http_version?: scalar|Param|null, // The default HTTP version, typically 1.1 or 2.0, leave to null for the best version.
|
||||
* resolve?: array<string, scalar|Param|null>,
|
||||
* proxy?: scalar|Param|null, // The URL of the proxy to pass requests through or null for automatic detection.
|
||||
* no_proxy?: scalar|Param|null, // A comma separated list of hosts that do not require a proxy to be reached.
|
||||
* timeout?: float|Param, // The idle timeout, defaults to the "default_socket_timeout" ini parameter.
|
||||
* max_duration?: float|Param, // The maximum execution time for the request+response as a whole.
|
||||
* bindto?: scalar|Param|null, // A network interface name, IP address, a host name or a UNIX socket to bind to.
|
||||
* verify_peer?: bool|Param, // Indicates if the peer should be verified in a TLS context.
|
||||
* verify_host?: bool|Param, // Indicates if the host should exist as a certificate common name.
|
||||
* cafile?: scalar|Param|null, // A certificate authority file.
|
||||
* capath?: scalar|Param|null, // A directory that contains multiple certificate authority files.
|
||||
* local_cert?: scalar|Param|null, // A PEM formatted certificate file.
|
||||
* local_pk?: scalar|Param|null, // A private key file.
|
||||
* passphrase?: scalar|Param|null, // The passphrase used to encrypt the "local_pk" file.
|
||||
* ciphers?: scalar|Param|null, // A list of TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...)
|
||||
* peer_fingerprint?: array{ // Associative array: hashing algorithm => hash(es).
|
||||
* sha1?: mixed,
|
||||
* pin-sha256?: mixed,
|
||||
* md5?: mixed,
|
||||
* },
|
||||
* crypto_method?: scalar|Param|null, // The minimum version of TLS to accept; must be one of STREAM_CRYPTO_METHOD_TLSv*_CLIENT constants.
|
||||
* extra?: array<string, mixed>,
|
||||
* rate_limiter?: scalar|Param|null, // Rate limiter name to use for throttling requests. // Default: null
|
||||
* caching?: bool|array{ // Caching configuration.
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* cache_pool?: string|Param, // The taggable cache pool to use for storing the responses. // Default: "cache.http_client"
|
||||
* shared?: bool|Param, // Indicates whether the cache is shared (public) or private. // Default: true
|
||||
* max_ttl?: int|Param, // The maximum TTL (in seconds) allowed for cached responses. Null means no cap. // Default: null
|
||||
* },
|
||||
* retry_failed?: bool|array{
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* retry_strategy?: scalar|Param|null, // service id to override the retry strategy. // Default: null
|
||||
* http_codes?: array<string, array{ // Default: []
|
||||
* code?: int|Param,
|
||||
* methods?: list<string|Param>,
|
||||
* }>,
|
||||
* max_retries?: int|Param, // Default: 3
|
||||
* delay?: int|Param, // Time in ms to delay (or the initial value when multiplier is used). // Default: 1000
|
||||
* multiplier?: float|Param, // If greater than 1, delay will grow exponentially for each retry: delay * (multiple ^ retries). // Default: 2
|
||||
* max_delay?: int|Param, // Max time in ms that a retry should ever be delayed (0 = infinite). // Default: 0
|
||||
* jitter?: float|Param, // Randomness in percent (between 0 and 1) to apply to the delay. // Default: 0.1
|
||||
* },
|
||||
* },
|
||||
* mock_response_factory?: scalar|Param|null, // The id of the service that should generate mock responses. It should be either an invokable or an iterable.
|
||||
* scoped_clients?: array<string, string|array{ // Default: []
|
||||
* scope?: scalar|Param|null, // The regular expression that the request URL must match before adding the other options. When none is provided, the base URI is used instead.
|
||||
* base_uri?: scalar|Param|null, // The URI to resolve relative URLs, following rules in RFC 3985, section 2.
|
||||
* auth_basic?: scalar|Param|null, // An HTTP Basic authentication "username:password".
|
||||
* auth_bearer?: scalar|Param|null, // A token enabling HTTP Bearer authorization.
|
||||
* auth_ntlm?: scalar|Param|null, // A "username:password" pair to use Microsoft NTLM authentication (requires the cURL extension).
|
||||
* query?: array<string, scalar|Param|null>,
|
||||
* headers?: array<string, mixed>,
|
||||
* max_redirects?: int|Param, // The maximum number of redirects to follow.
|
||||
* http_version?: scalar|Param|null, // The default HTTP version, typically 1.1 or 2.0, leave to null for the best version.
|
||||
* resolve?: array<string, scalar|Param|null>,
|
||||
* proxy?: scalar|Param|null, // The URL of the proxy to pass requests through or null for automatic detection.
|
||||
* no_proxy?: scalar|Param|null, // A comma separated list of hosts that do not require a proxy to be reached.
|
||||
* timeout?: float|Param, // The idle timeout, defaults to the "default_socket_timeout" ini parameter.
|
||||
* max_duration?: float|Param, // The maximum execution time for the request+response as a whole.
|
||||
* bindto?: scalar|Param|null, // A network interface name, IP address, a host name or a UNIX socket to bind to.
|
||||
* verify_peer?: bool|Param, // Indicates if the peer should be verified in a TLS context.
|
||||
* verify_host?: bool|Param, // Indicates if the host should exist as a certificate common name.
|
||||
* cafile?: scalar|Param|null, // A certificate authority file.
|
||||
* capath?: scalar|Param|null, // A directory that contains multiple certificate authority files.
|
||||
* local_cert?: scalar|Param|null, // A PEM formatted certificate file.
|
||||
* local_pk?: scalar|Param|null, // A private key file.
|
||||
* passphrase?: scalar|Param|null, // The passphrase used to encrypt the "local_pk" file.
|
||||
* ciphers?: scalar|Param|null, // A list of TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...).
|
||||
* peer_fingerprint?: array{ // Associative array: hashing algorithm => hash(es).
|
||||
* sha1?: mixed,
|
||||
* pin-sha256?: mixed,
|
||||
* md5?: mixed,
|
||||
* },
|
||||
* crypto_method?: scalar|Param|null, // The minimum version of TLS to accept; must be one of STREAM_CRYPTO_METHOD_TLSv*_CLIENT constants.
|
||||
* extra?: array<string, mixed>,
|
||||
* rate_limiter?: scalar|Param|null, // Rate limiter name to use for throttling requests. // Default: null
|
||||
* caching?: bool|array{ // Caching configuration.
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* cache_pool?: string|Param, // The taggable cache pool to use for storing the responses. // Default: "cache.http_client"
|
||||
* shared?: bool|Param, // Indicates whether the cache is shared (public) or private. // Default: true
|
||||
* max_ttl?: int|Param, // The maximum TTL (in seconds) allowed for cached responses. Null means no cap. // Default: null
|
||||
* },
|
||||
* retry_failed?: bool|array{
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* retry_strategy?: scalar|Param|null, // service id to override the retry strategy. // Default: null
|
||||
* http_codes?: array<string, array{ // Default: []
|
||||
* code?: int|Param,
|
||||
* methods?: list<string|Param>,
|
||||
* }>,
|
||||
* max_retries?: int|Param, // Default: 3
|
||||
* delay?: int|Param, // Time in ms to delay (or the initial value when multiplier is used). // Default: 1000
|
||||
* multiplier?: float|Param, // If greater than 1, delay will grow exponentially for each retry: delay * (multiple ^ retries). // Default: 2
|
||||
* max_delay?: int|Param, // Max time in ms that a retry should ever be delayed (0 = infinite). // Default: 0
|
||||
* jitter?: float|Param, // Randomness in percent (between 0 and 1) to apply to the delay. // Default: 0.1
|
||||
* },
|
||||
* }>,
|
||||
* },
|
||||
* mailer?: bool|array{ // Mailer configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* message_bus?: scalar|Param|null, // The message bus to use. Defaults to the default bus if the Messenger component is installed. // Default: null
|
||||
* dsn?: scalar|Param|null, // Default: null
|
||||
* transports?: array<string, scalar|Param|null>,
|
||||
* envelope?: array{ // Mailer Envelope configuration
|
||||
* sender?: scalar|Param|null,
|
||||
* recipients?: list<scalar|Param|null>,
|
||||
* allowed_recipients?: list<scalar|Param|null>,
|
||||
* },
|
||||
* headers?: array<string, string|array{ // Default: []
|
||||
* value?: mixed,
|
||||
* }>,
|
||||
* dkim_signer?: bool|array{ // DKIM signer configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* key?: scalar|Param|null, // Key content, or path to key (in PEM format with the `file://` prefix) // Default: ""
|
||||
* domain?: scalar|Param|null, // Default: ""
|
||||
* select?: scalar|Param|null, // Default: ""
|
||||
* passphrase?: scalar|Param|null, // The private key passphrase // Default: ""
|
||||
* options?: array<string, mixed>,
|
||||
* },
|
||||
* smime_signer?: bool|array{ // S/MIME signer configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* key?: scalar|Param|null, // Path to key (in PEM format) // Default: ""
|
||||
* certificate?: scalar|Param|null, // Path to certificate (in PEM format without the `file://` prefix) // Default: ""
|
||||
* passphrase?: scalar|Param|null, // The private key passphrase // Default: null
|
||||
* extra_certificates?: scalar|Param|null, // Default: null
|
||||
* sign_options?: int|Param, // Default: null
|
||||
* },
|
||||
* smime_encrypter?: bool|array{ // S/MIME encrypter configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* repository?: scalar|Param|null, // S/MIME certificate repository service. This service shall implement the `Symfony\Component\Mailer\EventListener\SmimeCertificateRepositoryInterface`. // Default: ""
|
||||
* cipher?: int|Param, // A set of algorithms used to encrypt the message // Default: null
|
||||
* },
|
||||
* },
|
||||
* secrets?: bool|array{
|
||||
* enabled?: bool|Param, // Default: true
|
||||
* vault_directory?: scalar|Param|null, // Default: "%kernel.project_dir%/config/secrets/%kernel.runtime_environment%"
|
||||
* local_dotenv_file?: scalar|Param|null, // Default: "%kernel.project_dir%/.env.%kernel.environment%.local"
|
||||
* decryption_env_var?: scalar|Param|null, // Default: "base64:default::SYMFONY_DECRYPTION_SECRET"
|
||||
* },
|
||||
* notifier?: bool|array{ // Notifier configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* message_bus?: scalar|Param|null, // The message bus to use. Defaults to the default bus if the Messenger component is installed. // Default: null
|
||||
* chatter_transports?: array<string, scalar|Param|null>,
|
||||
* texter_transports?: array<string, scalar|Param|null>,
|
||||
* notification_on_failed_messages?: bool|Param, // Default: false
|
||||
* channel_policy?: array<string, string|list<scalar|Param|null>>,
|
||||
* admin_recipients?: list<array{ // Default: []
|
||||
* email?: scalar|Param|null,
|
||||
* phone?: scalar|Param|null, // Default: ""
|
||||
* }>,
|
||||
* },
|
||||
* rate_limiter?: bool|array{ // Rate limiter configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* limiters?: array<string, array{ // Default: []
|
||||
* lock_factory?: scalar|Param|null, // The service ID of the lock factory used by this limiter (or null to disable locking). // Default: "auto"
|
||||
* cache_pool?: scalar|Param|null, // The cache pool to use for storing the current limiter state. // Default: "cache.rate_limiter"
|
||||
* storage_service?: scalar|Param|null, // The service ID of a custom storage implementation, this precedes any configured "cache_pool". // Default: null
|
||||
* policy?: "fixed_window"|"token_bucket"|"sliding_window"|"compound"|"no_limit"|Param, // The algorithm to be used by this limiter.
|
||||
* limiters?: list<scalar|Param|null>,
|
||||
* limit?: int|Param, // The maximum allowed hits in a fixed interval or burst.
|
||||
* interval?: scalar|Param|null, // Configures the fixed interval if "policy" is set to "fixed_window" or "sliding_window". The value must be a number followed by "second", "minute", "hour", "day", "week" or "month" (or their plural equivalent).
|
||||
* rate?: array{ // Configures the fill rate if "policy" is set to "token_bucket".
|
||||
* interval?: scalar|Param|null, // Configures the rate interval. The value must be a number followed by "second", "minute", "hour", "day", "week" or "month" (or their plural equivalent).
|
||||
* amount?: int|Param, // Amount of tokens to add each interval. // Default: 1
|
||||
* },
|
||||
* }>,
|
||||
* },
|
||||
* uid?: bool|array{ // Uid configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* default_uuid_version?: 7|6|4|1|Param, // Default: 7
|
||||
* name_based_uuid_version?: 5|3|Param, // Default: 5
|
||||
* name_based_uuid_namespace?: scalar|Param|null,
|
||||
* time_based_uuid_version?: 7|6|1|Param, // Default: 7
|
||||
* time_based_uuid_node?: scalar|Param|null,
|
||||
* },
|
||||
* html_sanitizer?: bool|array{ // HtmlSanitizer configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* sanitizers?: array<string, array{ // Default: []
|
||||
* allow_safe_elements?: bool|Param, // Allows "safe" elements and attributes. // Default: false
|
||||
* allow_static_elements?: bool|Param, // Allows all static elements and attributes from the W3C Sanitizer API standard. // Default: false
|
||||
* allow_elements?: array<string, mixed>,
|
||||
* block_elements?: list<string|Param>,
|
||||
* drop_elements?: list<string|Param>,
|
||||
* allow_attributes?: array<string, mixed>,
|
||||
* drop_attributes?: array<string, mixed>,
|
||||
* force_attributes?: array<string, array<string, string|Param>>,
|
||||
* force_https_urls?: bool|Param, // Transforms URLs using the HTTP scheme to use the HTTPS scheme instead. // Default: false
|
||||
* allowed_link_schemes?: list<string|Param>,
|
||||
* allowed_link_hosts?: list<string|Param>|null,
|
||||
* allow_relative_links?: bool|Param, // Allows relative URLs to be used in links href attributes. // Default: false
|
||||
* allowed_media_schemes?: list<string|Param>,
|
||||
* allowed_media_hosts?: list<string|Param>|null,
|
||||
* allow_relative_medias?: bool|Param, // Allows relative URLs to be used in media source attributes (img, audio, video, ...). // Default: false
|
||||
* with_attribute_sanitizers?: list<string|Param>,
|
||||
* without_attribute_sanitizers?: list<string|Param>,
|
||||
* max_input_length?: int|Param, // The maximum length allowed for the sanitized input. // Default: 0
|
||||
* }>,
|
||||
* },
|
||||
* webhook?: bool|array{ // Webhook configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* message_bus?: scalar|Param|null, // The message bus to use. // Default: "messenger.default_bus"
|
||||
* routing?: array<string, array{ // Default: []
|
||||
* service?: scalar|Param|null,
|
||||
* secret?: scalar|Param|null, // Default: ""
|
||||
* }>,
|
||||
* },
|
||||
* remote-event?: bool|array{ // RemoteEvent configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* },
|
||||
* json_streamer?: bool|array{ // JSON streamer configuration
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* },
|
||||
* }
|
||||
* @psalm-type TwigConfig = array{
|
||||
* form_themes?: list<scalar|Param|null>,
|
||||
* globals?: array<string, array{ // Default: []
|
||||
* id?: scalar|Param|null,
|
||||
* type?: scalar|Param|null,
|
||||
* value?: mixed,
|
||||
* }>,
|
||||
* autoescape_service?: scalar|Param|null, // Default: null
|
||||
* autoescape_service_method?: scalar|Param|null, // Default: null
|
||||
* cache?: scalar|Param|null, // Default: true
|
||||
* charset?: scalar|Param|null, // Default: "%kernel.charset%"
|
||||
* debug?: bool|Param, // Default: "%kernel.debug%"
|
||||
* strict_variables?: bool|Param, // Default: "%kernel.debug%"
|
||||
* auto_reload?: scalar|Param|null,
|
||||
* optimizations?: int|Param,
|
||||
* default_path?: scalar|Param|null, // The default path used to load templates. // Default: "%kernel.project_dir%/templates"
|
||||
* file_name_pattern?: list<scalar|Param|null>,
|
||||
* paths?: array<string, mixed>,
|
||||
* date?: array{ // The default format options used by the date filter.
|
||||
* format?: scalar|Param|null, // Default: "F j, Y H:i"
|
||||
* interval_format?: scalar|Param|null, // Default: "%d days"
|
||||
* timezone?: scalar|Param|null, // The timezone used when formatting dates, when set to null, the timezone returned by date_default_timezone_get() is used. // Default: null
|
||||
* },
|
||||
* number_format?: array{ // The default format options for the number_format filter.
|
||||
* decimals?: int|Param, // Default: 0
|
||||
* decimal_point?: scalar|Param|null, // Default: "."
|
||||
* thousands_separator?: scalar|Param|null, // Default: ","
|
||||
* },
|
||||
* mailer?: array{
|
||||
* html_to_text_converter?: scalar|Param|null, // A service implementing the "Symfony\Component\Mime\HtmlToTextConverter\HtmlToTextConverterInterface". // Default: null
|
||||
* },
|
||||
* }
|
||||
* @psalm-type TwigExtraConfig = array{
|
||||
* cache?: bool|array{
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* },
|
||||
* html?: bool|array{
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* },
|
||||
* markdown?: bool|array{
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* },
|
||||
* intl?: bool|array{
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* },
|
||||
* cssinliner?: bool|array{
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* },
|
||||
* inky?: bool|array{
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* },
|
||||
* string?: bool|array{
|
||||
* enabled?: bool|Param, // Default: false
|
||||
* },
|
||||
* commonmark?: array{
|
||||
* renderer?: array{ // Array of options for rendering HTML.
|
||||
* block_separator?: scalar|Param|null,
|
||||
* inner_separator?: scalar|Param|null,
|
||||
* soft_break?: scalar|Param|null,
|
||||
* },
|
||||
* html_input?: "strip"|"allow"|"escape"|Param, // How to handle HTML input.
|
||||
* allow_unsafe_links?: bool|Param, // Remove risky link and image URLs by setting this to false. // Default: true
|
||||
* max_nesting_level?: int|Param, // The maximum nesting level for blocks. // Default: 9223372036854775807
|
||||
* max_delimiters_per_line?: int|Param, // The maximum number of strong/emphasis delimiters per line. // Default: 9223372036854775807
|
||||
* slug_normalizer?: array{ // Array of options for configuring how URL-safe slugs are created.
|
||||
* instance?: mixed,
|
||||
* max_length?: int|Param, // Default: 255
|
||||
* unique?: mixed,
|
||||
* },
|
||||
* commonmark?: array{ // Array of options for configuring the CommonMark core extension.
|
||||
* enable_em?: bool|Param, // Default: true
|
||||
* enable_strong?: bool|Param, // Default: true
|
||||
* use_asterisk?: bool|Param, // Default: true
|
||||
* use_underscore?: bool|Param, // Default: true
|
||||
* unordered_list_markers?: list<scalar|Param|null>,
|
||||
* },
|
||||
* ...<mixed>
|
||||
* },
|
||||
* }
|
||||
* @psalm-type MakerConfig = array{
|
||||
* root_namespace?: scalar|Param|null, // Default: "App"
|
||||
* generate_final_classes?: bool|Param, // Default: true
|
||||
* generate_final_entities?: bool|Param, // Default: false
|
||||
* }
|
||||
* @psalm-type UxIconsConfig = array{
|
||||
* icon_dir?: scalar|Param|null, // The local directory where icons are stored. // Default: "%kernel.project_dir%/assets/icons"
|
||||
* default_icon_attributes?: array<string, scalar|Param|null>,
|
||||
* icon_sets?: array<string, array{ // the icon set prefix (e.g. "acme") // Default: []
|
||||
* path?: scalar|Param|null, // The local icon set directory path. (cannot be used with 'alias')
|
||||
* alias?: scalar|Param|null, // The remote icon set identifier. (cannot be used with 'path')
|
||||
* icon_attributes?: array<string, scalar|Param|null>,
|
||||
* suffixes?: array<string, array{ // The suffix name (e.g. "solid", "20-solid") // Default: []
|
||||
* icon_attributes?: array<string, scalar|Param|null>,
|
||||
* }>,
|
||||
* }>,
|
||||
* aliases?: array<string, string|Param>,
|
||||
* iconify?: bool|array{ // Configuration for the remote icon service.
|
||||
* enabled?: bool|Param, // Default: true
|
||||
* on_demand?: bool|Param, // Whether to download icons "on demand". // Default: true
|
||||
* endpoint?: scalar|Param|null, // The endpoint for the Iconify icons API. // Default: "https://api.iconify.design"
|
||||
* },
|
||||
* ignore_not_found?: bool|Param, // Ignore error when an icon is not found. Set to 'true' to fail silently. // Default: false
|
||||
* }
|
||||
* @psalm-type StimulusConfig = array{
|
||||
* controller_paths?: list<scalar|Param|null>,
|
||||
* controllers_json?: scalar|Param|null, // Default: "%kernel.project_dir%/assets/controllers.json"
|
||||
* }
|
||||
* @psalm-type ConfigType = array{
|
||||
* imports?: ImportsConfig,
|
||||
* parameters?: ParametersConfig,
|
||||
* services?: ServicesConfig,
|
||||
* framework?: FrameworkConfig,
|
||||
* twig?: TwigConfig,
|
||||
* twig_extra?: TwigExtraConfig,
|
||||
* ux_icons?: UxIconsConfig,
|
||||
* stimulus?: StimulusConfig,
|
||||
* "when@dev"?: array{
|
||||
* imports?: ImportsConfig,
|
||||
* parameters?: ParametersConfig,
|
||||
* services?: ServicesConfig,
|
||||
* framework?: FrameworkConfig,
|
||||
* twig?: TwigConfig,
|
||||
* twig_extra?: TwigExtraConfig,
|
||||
* maker?: MakerConfig,
|
||||
* ux_icons?: UxIconsConfig,
|
||||
* stimulus?: StimulusConfig,
|
||||
* },
|
||||
* "when@prod"?: array{
|
||||
* imports?: ImportsConfig,
|
||||
* parameters?: ParametersConfig,
|
||||
* services?: ServicesConfig,
|
||||
* framework?: FrameworkConfig,
|
||||
* twig?: TwigConfig,
|
||||
* twig_extra?: TwigExtraConfig,
|
||||
* ux_icons?: UxIconsConfig,
|
||||
* stimulus?: StimulusConfig,
|
||||
* },
|
||||
* "when@test"?: array{
|
||||
* imports?: ImportsConfig,
|
||||
* parameters?: ParametersConfig,
|
||||
* services?: ServicesConfig,
|
||||
* framework?: FrameworkConfig,
|
||||
* twig?: TwigConfig,
|
||||
* twig_extra?: TwigExtraConfig,
|
||||
* ux_icons?: UxIconsConfig,
|
||||
* stimulus?: StimulusConfig,
|
||||
* },
|
||||
* ...<string, ExtensionType|array{ // extra keys must follow the when@%env% pattern or match an extension alias
|
||||
* imports?: ImportsConfig,
|
||||
* parameters?: ParametersConfig,
|
||||
* services?: ServicesConfig,
|
||||
* ...<string, ExtensionType>,
|
||||
* }>
|
||||
* }
|
||||
*/
|
||||
final class App
|
||||
{
|
||||
/**
|
||||
* @param ConfigType $config
|
||||
*
|
||||
* @psalm-return ConfigType
|
||||
*/
|
||||
public static function config(array $config): array
|
||||
{
|
||||
/** @var ConfigType $config */
|
||||
$config = AppReference::config($config);
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Symfony\Component\Routing\Loader\Configurator;
|
||||
|
||||
/**
|
||||
* This class provides array-shapes for configuring the routes of an application.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```php
|
||||
* // config/routes.php
|
||||
* namespace Symfony\Component\Routing\Loader\Configurator;
|
||||
*
|
||||
* return Routes::config([
|
||||
* 'controllers' => [
|
||||
* 'resource' => 'routing.controllers',
|
||||
* ],
|
||||
* ]);
|
||||
* ```
|
||||
*
|
||||
* @psalm-type RouteConfig = array{
|
||||
* path: string|array<string,string>,
|
||||
* controller?: string,
|
||||
* methods?: string|list<string>,
|
||||
* requirements?: array<string,string>,
|
||||
* defaults?: array<string,mixed>,
|
||||
* options?: array<string,mixed>,
|
||||
* host?: string|array<string,string>,
|
||||
* schemes?: string|list<string>,
|
||||
* condition?: string,
|
||||
* locale?: string,
|
||||
* format?: string,
|
||||
* utf8?: bool,
|
||||
* stateless?: bool,
|
||||
* }
|
||||
* @psalm-type ImportConfig = array{
|
||||
* resource: string,
|
||||
* type?: string,
|
||||
* exclude?: string|list<string>,
|
||||
* prefix?: string|array<string,string>,
|
||||
* name_prefix?: string,
|
||||
* trailing_slash_on_root?: bool,
|
||||
* controller?: string,
|
||||
* methods?: string|list<string>,
|
||||
* requirements?: array<string,string>,
|
||||
* defaults?: array<string,mixed>,
|
||||
* options?: array<string,mixed>,
|
||||
* host?: string|array<string,string>,
|
||||
* schemes?: string|list<string>,
|
||||
* condition?: string,
|
||||
* locale?: string,
|
||||
* format?: string,
|
||||
* utf8?: bool,
|
||||
* stateless?: bool,
|
||||
* }
|
||||
* @psalm-type AliasConfig = array{
|
||||
* alias: string,
|
||||
* deprecated?: array{package:string, version:string, message?:string},
|
||||
* }
|
||||
* @psalm-type RoutesConfig = array{
|
||||
* "when@dev"?: array<string, RouteConfig|ImportConfig|AliasConfig>,
|
||||
* "when@prod"?: array<string, RouteConfig|ImportConfig|AliasConfig>,
|
||||
* "when@test"?: array<string, RouteConfig|ImportConfig|AliasConfig>,
|
||||
* ...<string, RouteConfig|ImportConfig|AliasConfig>
|
||||
* }
|
||||
*/
|
||||
final class Routes
|
||||
{
|
||||
/**
|
||||
* @param RoutesConfig $config
|
||||
*
|
||||
* @psalm-return RoutesConfig
|
||||
*/
|
||||
public static function config(array $config): array
|
||||
{
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -35,4 +35,11 @@ return [
|
||||
'@symfony/stimulus-bundle' => [
|
||||
'path' => './vendor/symfony/stimulus-bundle/assets/dist/loader.js',
|
||||
],
|
||||
'aos' => [
|
||||
'version' => '2.3.4',
|
||||
],
|
||||
'aos/dist/aos.css' => [
|
||||
'version' => '2.3.4',
|
||||
'type' => 'css',
|
||||
],
|
||||
];
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<header class="border-bottom border-white border-opacity-10">
|
||||
<nav class="navbar navbar-expand-lg navbar-dark">
|
||||
<div class="container py-3">
|
||||
<header class="border-bottom sticky-top">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="{{ path('homepage') }}">
|
||||
{{ ux_icon('logos:symfony-ai', {width: 225, height: 64}) }}
|
||||
{{ include('_symfony-ai-logo.svg') }}
|
||||
</a>
|
||||
|
||||
<button
|
||||
@@ -20,19 +20,33 @@
|
||||
<div class="collapse navbar-collapse" id="mainNavbar">
|
||||
<ul class="navbar-nav ms-auto align-items-lg-center gap-lg-2 mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="https://symfony.com/doc/current/ai/index.html">Documentation</a>
|
||||
<a class="nav-link" href="#components">Components</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="https://github.com/symfony/ai-demo">Demos</a>
|
||||
<a class="nav-link" href="#features">Features</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="https://symfony.com/doc/current/ai/cookbook/index.html">Cookbook</a>
|
||||
<a class="nav-link" href="#bridges">Bridges</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="https://github.com/symfony/ai/issues">Support</a>
|
||||
<a class="nav-link" href="#demos">Demos</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#mcp">MCP</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#mate">Mate</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#support">Support</a>
|
||||
</li>
|
||||
<li class="nav-item ms-lg-2">
|
||||
<a class="nav-link" href="https://github.com/symfony/ai">
|
||||
<a class="nav-link" href="https://symfony.com/doc/current/ai/index.html" aria-label="Documentation" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Documentation">
|
||||
{{ ux_icon('tabler:book', {width: 24, height: 24}) }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="https://github.com/symfony/ai" aria-label="GitHub" data-bs-toggle="tooltip" data-bs-placement="bottom" title="GitHub">
|
||||
{{ ux_icon('simple-icons:github', {width: 24, height: 24}) }}
|
||||
</a>
|
||||
</li>
|
||||
@@ -41,7 +55,7 @@
|
||||
<template id="icon-theme-light">{{ ux_icon('tabler:sun-filled', {width: 24, height: 24}) }}</template>
|
||||
<template id="icon-theme-dark">{{ ux_icon('tabler:moon', {width: 24, height: 24}) }}</template>
|
||||
|
||||
<button type="button" id="themeToggle" class="btn btn-link p-0 border-0" aria-label="Toggle theme">
|
||||
<button type="button" id="themeToggle" class="btn btn-link p-0 border-0" aria-label="Toggle theme" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Toggle theme">
|
||||
<span id="themeIcon">{{ ux_icon('tabler:contrast-filled', {width: 24, height: 24}) }}</span>
|
||||
</button>
|
||||
</li>
|
||||
|
||||
23
ai.symfony.com/templates/_symfony-ai-logo.svg
Normal file
23
ai.symfony.com/templates/_symfony-ai-logo.svg
Normal file
@@ -0,0 +1,23 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 284.45573 80.93233" width="180" height="50" role="img" aria-label="Symfony AI" class="logo">
|
||||
<rect class="logo-icon color-shift" x="14.58074" y="14.90988" width="51.11259" height="51.11258" rx="5.11118" ry="5.11118"/>
|
||||
<path class="logo-emblem"
|
||||
d="M53.08095,21.53551c-2.84376.09725-5.32638,1.6672-7.17431,3.83295-2.04605,2.37857-3.40682,5.19735-4.38874,8.07653-1.75354-1.43872-3.10577-3.30009-5.92154-4.10966-2.17539-.62552-4.45989-.36842-6.56144,1.19759-.99536.74316-1.68111,1.8658-2.007,2.9244-.84467,2.74494.88689,5.18929,1.674,6.0659l1.72112,1.8424c.35387.36193,1.2085,1.30479.79028,2.6556-.44985,1.47098-2.22188,2.42032-4.0393,1.86232-.81225-.24919-1.97822-.85352-1.71653-1.70357.10689-.34897.3564-.61144.49112-.90918.12175-.25995.1812-.45317.2182-.56891.33189-1.08264-.12238-2.49289-1.28297-2.85166-1.08359-.33237-2.19168-.0691-2.6216,1.32519-.48701,1.58419.27117,4.46005,4.33339,5.71139,4.75889,1.46465,8.78364-1.1277,9.35461-4.5059.36003-2.11594-.59642-3.68922-2.34648-5.71092l-1.42718-1.57881c-.86348-.86301-1.16043-2.33462-.26643-3.46516.75517-.95504,1.82896-1.36077,3.5904-.88262,2.57021.69698,3.71468,2.4804,5.62554,3.91912-.78775,2.5884-1.30432,5.1866-1.77061,7.51616l-.28635,1.7382c-1.36567,7.1634-2.40894,11.09881-5.11893,13.35737-.54662.38897-1.32693.97053-2.50286,1.01196-.61793.01882-.81716-.40637-.82554-.59216-.01407-.43166.35039-.63042.59247-.82459.36288-.19812.91045-.52558.87297-1.57533-.03953-1.24044-1.06667-2.31549-2.55219-2.26615-1.11221.03747-2.80771,1.08311-2.74336,2.99998.06546,1.98012,1.91023,3.46358,4.69264,3.36934,1.48711-.04981,4.80838-.65493,8.08033-4.54432,3.80923-4.46052,4.87432-9.57186,5.6755-13.31373l.89495-4.94073c.49634.06008,1.02793.1004,1.60601.11385,4.74435.1004,7.11581-2.35597,7.15281-4.14397.02404-1.08169-.70932-2.14741-1.73646-2.12195-.73383.02056-1.65724.51072-1.87797,1.526-.21773.99583,1.50892,1.89568.1597,2.77182-.95835.61998-2.67647,1.05607-5.09647.7022l.43973-2.43186c.89811-4.61121,2.00573-10.28276,6.2082-10.42143.30643-.01439,1.42607.01297,1.45216.75454.00743.24635-.05455.31118-.34359.8766-.29536.44131-.40684.81874-.39229,1.24993.04048,1.17656.93543,1.95134,2.23168,1.90628,1.73314-.05835,2.23074-1.74499,2.20228-2.61243-.07084-2.03862-2.21856-3.3265-5.05995-3.23257"/>
|
||||
<path class="logo-text"
|
||||
d="M179.41294,31.82535c6.47359,0,10.81349,4.6767,10.81349,11.1474,0,6.0967-4.4238,11.1456-10.81349,11.1456-6.42771,0-10.85161-5.0489-10.85161-11.1456,0-6.4707,4.3379-11.1474,10.85161-11.1474ZM179.41294,50.95035c4.59279,0,6.63669-4.1758,6.63669-7.9776,0-4.0478-2.4629-7.97162-6.63669-7.97162-4.21581,0-6.67971,3.92382-6.67971,7.97162,0,3.8018,2.0469,7.9776,6.67971,7.9776Z"/>
|
||||
<path class="logo-text"
|
||||
d="M168.02333,33.80775v-1.4814h-5.6377v-2.0245c0-2.8808.4189-5.0546,3.7978-5.0546.06251,0,.1279.0058.1934.0088.0068,0,.0049-.0108.0127-.0108.93841.0684,1.7197-.6953,1.77241-1.6328l.06839-1.2861c-.793-.126-1.6299-.25-2.6299-.25-5.8027,0-7.1377,3.38178-7.1377,8.5595v1.6905h-5.0146v1.6533c.1289.8574.8632,1.5176,1.75781,1.5176.00389,0,.00879.00388.01359.00388h3.2432v18.11912h2.167c.9004,0,1.6338-.6777,1.7559-1.541v-16.57812h3.9257c.9229-.03028,1.6661-.76958,1.712-1.69338Z"/>
|
||||
<path class="logo-text"
|
||||
d="M115.74743,32.32635c-.0059,0-.0108.00478-.021.00478-.8262,0-1.627.56842-1.9766,1.337l-5.3994,16.27832h-.08449l-5.28121-16.2696c-.3457-.7734-1.1504-1.34572-1.9819-1.34572-.0103,0-.0132-.00478-.0206-.00478h-2.7368l7.2227,19.874c.2524.7109.7539,1.7969.7539,2.2539,0,.41988-1.1699,5.2598-4.6792,5.2598-.08589,0-.1743-.0098-.2593-.0157-.895-.0488-1.5708.5762-1.7202,1.5391l-.10739,1.3594c.71039.125,1.42229.2929,2.67379.2929,5.1763,0,6.7207-4.7207,8.1841-8.8105l7.8472-21.7529h-2.4136Z"/>
|
||||
<path class="logo-text"
|
||||
d="M90.79913,36.65155c-3.24019-1.6582-6.7871-2.7852-6.8486-6.1211.0098-3.543,3.2661-4.4737,5.7739-4.4707.0122-.002.022-.002.0284-.002,1.0903,0,1.9614.10838,2.812.2949.0112,0,.0092-.0136.0219-.0136.8999.0664,1.6553-.6368,1.7598-1.5235l.0718-1.2871c-1.6446-.4082-3.3638-.6172-4.86039-.6172-5.45511.0342-9.50631,2.7793-9.519,8.0176.00629,4.5801,3.09179,6.3555,6.414,7.9629,3.25639,1.56638,6.82129,2.85838,6.85209,6.6387-.01809,3.9433-3.8242,5.3964-6.4468,5.4023-1.5342-.0059-3.1992-.3867-4.6025-.8301-.8789-.14452-1.5996.6309-1.7149,1.6582l-.1176,1.1465c1.9292.6231,3.9062,1.168,5.895,1.168h.022c6.1123-.0449,10.8618-2.4824,10.8808-8.8106-.0083-4.8828-3.1172-6.93362-6.4219-8.6132Z"/>
|
||||
<path class="logo-text"
|
||||
d="M150.15613,53.62025h.0049c.8466,0,1.5537-.5957,1.7314-1.3887v-12.2636c0-4.63482-1.958-8.1426-7.3887-8.1426-1.9199,0-5.13379,1.0879-6.5957,4.13478-1.126-2.88088-3.5898-4.13478-6.0508-4.13478-3.1328,0-5.2627,1.1269-6.7646,3.59178h-.084v-1.32418c-.01169-.9746-.8008-1.76182-1.7773-1.76182-.0088,0-.0127-.00478-.0186-.00478h-1.8774v21.2939h2.1284c.9844,0,1.7803-.7988,1.7803-1.7812,0-.0274.0088-.0372.0175-.0547v-8.8946c0-3.9648,1.585-7.88862,5.5918-7.88862,3.1709,0,3.8018,3.29682,3.8018,5.92772v12.6914h2.1592c.9297,0,1.6787-.7148,1.7617-1.623v-9.1075c0-3.9648,1.58791-7.88862,5.5947-7.88862,3.1729,0,3.7998,3.29682,3.7998,5.92772v12.6914h2.1856Z"/>
|
||||
<path class="logo-text"
|
||||
d="M210.80353,53.62025h.0049c.957,0,1.7276-.7559,1.77351-1.70312v-10.94728c0-5.71872-2.46591-9.1445-7.97661-9.1445-2.9639,0-5.8008,1.4629-6.9727,3.8857h-.082v-1.5908c0-.0019-.0039-.0019-.0039-.0059,0-.9873-.79879-1.78322-1.7793-1.78322-.0107,0-.0127-.00478-.01949-.00478h-1.99811v21.2939h2.1612c.9335,0,1.6914-.7266,1.7627-1.6426v-8.2109c0-5.1758,2.0058-8.76562,6.4726-8.76562,3.4268.207,4.5068,2.62692,4.5068,7.59662v11.0225h2.1504Z"/>
|
||||
<path class="logo-text"
|
||||
d="M231.93153,32.32635c-.0098,0-.0137.00478-.0225.00478-.82709,0-1.6289.56842-1.9775,1.337l-5.3975,16.27832h-.0879l-5.28219-16.2696c-.34571-.7734-1.15041-1.34572-1.98051-1.34572-.0078,0-.0127-.00478-.01949-.00478h-2.73731l7.2236,19.874c.25.7109.7549,1.7969.7549,2.2539,0,.41988-1.1709,5.2598-4.6807,5.2598-.0849,0-.1709-.0098-.2588-.0157-.8945-.0488-1.5683.5762-1.7216,1.5391l-.1055,1.3594c.709.125,1.4219.2929,2.6748.2929,5.1758,0,6.7197-4.7207,8.18361-8.8105l7.84859-21.7529h-2.414Z"/>
|
||||
<path class="logo-ai color-shift"
|
||||
d="M253.39985,47.45406h-13.29523l-1.60193,4.55023c-.40867,1.16081-1.50535,1.93738-2.73599,1.93738h-3.3834l10.92086-30.52647c.27304-.7632.99618-1.27253,1.80675-1.27253h3.32851c.81057,0,1.53372.50932,1.80675,1.27253l10.92086,30.52647h-3.0259c-1.47233,0-2.78439-.92908-3.27331-2.31786l-1.46798-4.16975ZM251.93835,43.20509l-5.16318-14.75776-5.20802,14.75776h10.3712Z"/>
|
||||
<path class="logo-ai color-shift"
|
||||
d="M269.87499,24.50757v27.11454c0,1.28105-1.0385,2.31955-2.31955,2.31955h-2.88949v-31.75364h2.88949c1.28105,0,2.31955,1.0385,2.31955,2.31955Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.6 KiB |
@@ -39,17 +39,17 @@
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="d-flex justify-content-md-end gap-3 align-items-center">
|
||||
<a href="https://symfony.com/doc/current/ai/index.html" class="footer-meta text-decoration-none">Docs</a>
|
||||
<a href="https://github.com/symfony/ai/commits/main/" target="_blank" class="footer-meta text-decoration-none">Changelog</a>
|
||||
<a href="https://github.com/symfony/ai/issues" target="_blank" class="footer-meta text-decoration-none">Support</a>
|
||||
<a href="https://symfony.com/doc/current/ai/index.html" class="footer-meta text-decoration-none">Documentation</a>
|
||||
<a href="https://github.com/symfony/ai/releases" target="_blank" rel="noopener noreferrer" class="footer-meta text-decoration-none">Releases</a>
|
||||
<a href="https://github.com/symfony/ai/issues" target="_blank" rel="noopener noreferrer" class="footer-meta text-decoration-none">Support</a>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="https://symfony.com" target="_blank" class="footer-meta text-decoration-none">
|
||||
<a href="https://symfony.com" target="_blank" rel="noopener noreferrer" class="footer-meta text-decoration-none">
|
||||
{{ ux_icon('simple-icons:symfony', {width: 16, height: 16, class: 'ms-1'}) }}
|
||||
</a>
|
||||
<a href="https://x.com/symfony" target="_blank" class="footer-meta text-decoration-none">
|
||||
<a href="https://x.com/symfony" target="_blank" rel="noopener noreferrer" class="footer-meta text-decoration-none">
|
||||
{{ ux_icon('simple-icons:x', {width: 16, height: 16, class: 'ms-1'}) }}
|
||||
</a>
|
||||
<a href="https://github.com/symfony/ai" target="_blank" class="footer-meta text-decoration-none">
|
||||
<a href="https://github.com/symfony/ai" target="_blank" rel="noopener noreferrer" class="footer-meta text-decoration-none">
|
||||
{{ ux_icon('simple-icons:github', {width: 16, height: 16, class: 'ms-1'}) }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
42
ai.symfony.com/templates/hero/_slide_agent.html.twig
Normal file
42
ai.symfony.com/templates/hero/_slide_agent.html.twig
Normal file
@@ -0,0 +1,42 @@
|
||||
<div class="row align-items-center">
|
||||
<div class="col-xl-6 pe-5">
|
||||
<h1 class="ff-title text-balance display-5 fw-bold mb-3">
|
||||
Build <span class="hero-highlight">AI-powered</span>
|
||||
applications with PHP
|
||||
</h1>
|
||||
<p class="text-balance fs-5 mb-4 opacity-75">
|
||||
Agents, tool calling, RAG, and multi-modal support — built on Symfony components.
|
||||
</p>
|
||||
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<a href="https://symfony.com/doc/current/ai/components/agent.html" class="btn btn-light btn-lg px-3 d-flex align-items-center">
|
||||
<span>Agent component</span>
|
||||
{{ ux_icon('tabler:arrow-up-right', {width: 24, height: 24, class: 'ms-2'}) }}
|
||||
</a>
|
||||
<a href="https://symfony.com/doc/current/ai/components/platform.html" class="mt-3 mt-sm-0 ms-lg-3 btn btn-outline-light btn-lg px-3 d-flex align-items-center">
|
||||
Platform component
|
||||
{{ ux_icon('tabler:arrow-up-right', {width: 24, height: 24, class: 'ms-2'}) }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-6 mt-5 mt-xl-0">
|
||||
{% set model_typed = stimulus_controller('typed', {
|
||||
loop: true,
|
||||
cursorChar: '|',
|
||||
strings: ['gpt-5.1', 'mistral-large-latest', 'gemini-3-pro-preview', 'claude-sonnet-4-5'],
|
||||
}) %}
|
||||
<pre class="terminal"><code><span class="variable">$platform</span> = <span class="title class_">PlatformFactory</span>::<span class="title function_ invoke__">create</span>(...);
|
||||
<span class="variable">$agent</span> = <span class="keyword">new</span> <span class="title class_">Agent</span>(<span class="variable">$platform</span>, <span class="string">'</span><span class="string" {{ model_typed|raw }}></span><span class="string">'</span>)
|
||||
|
||||
<span class="variable">$messages</span> = <span class="keyword">new</span> <span class="title class_">MessageBag</span>(
|
||||
<span class="title class_">Message</span>::<span class="title function_ invoke__">forSystem</span>(<span class="string">'You are a pirate and you write funny.'</span>),
|
||||
<span class="title class_">Message</span>::<span class="title function_ invoke__">ofUser</span>(<span class="string">'What is the Symfony framework?'</span>),
|
||||
);
|
||||
<span class="variable">$response</span> = <span class="variable">$agent</span>-><span class="title function_ invoke__">call</span>(<span class="variable">$messages</span>, [
|
||||
<span class="string">'temperature'</span> => 1.0, // specific options just for this call
|
||||
]);
|
||||
|
||||
<span class="keyword">echo</span> <span class="variable">$response</span>-><span class="title function_ invoke__">getContent</span>();</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
32
ai.symfony.com/templates/hero/_slide_mcp.html.twig
Normal file
32
ai.symfony.com/templates/hero/_slide_mcp.html.twig
Normal file
@@ -0,0 +1,32 @@
|
||||
<div class="row align-items-center">
|
||||
<div class="col-xl-6 pe-5">
|
||||
<h2 class="ff-title text-balance display-5 fw-bold mb-3">
|
||||
Build <span class="hero-highlight">MCP</span> Servers & Clients with PHP
|
||||
</h2>
|
||||
<p class="text-balance fs-5 mb-4 opacity-75">
|
||||
Expose tools to AI agents or connect to any MCP server — powered by Symfony.
|
||||
</p>
|
||||
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<a href="https://symfony.com/doc/current/ai/bundles/mcp-bundle.html" class="btn btn-light btn-lg px-3 d-flex align-items-center">
|
||||
<span>MCP Bundle docs</span>
|
||||
{{ ux_icon('tabler:arrow-up-right', {width: 24, height: 24, class: 'ms-2'}) }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-6 mt-5 mt-xl-0">
|
||||
<pre class="terminal"><code><span class="keyword">class</span> <span class="title class_">CurrentTimeTool</span>
|
||||
{
|
||||
<span class="comment">/**
|
||||
* @param string $format e.g. "Y-m-d H:i:s"
|
||||
*/</span>
|
||||
<span class="title">#[McpTool(name: 'current-time')]</span>
|
||||
<span class="keyword">public function</span> <span class="title function_ invoke__">getCurrentTime</span>(<span class="variable">string</span> <span class="variable">$format</span>): <span class="variable">string</span>
|
||||
{
|
||||
<span class="keyword">return</span> (<span class="keyword">new</span> <span class="title class_">\DateTime</span>(<span class="string">'now'</span>, <span class="keyword">new</span> <span class="title class_">\DateTimeZone</span>(<span class="string">'UTC'</span>)))
|
||||
-><span class="title function_ invoke__">format</span>(<span class="variable">$format</span>);
|
||||
}
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
32
ai.symfony.com/templates/hero/_slide_symfony.html.twig
Normal file
32
ai.symfony.com/templates/hero/_slide_symfony.html.twig
Normal file
@@ -0,0 +1,32 @@
|
||||
<div class="row align-items-center">
|
||||
<div class="col-xl-6 pe-5">
|
||||
<h2 class="ff-title text-balance display-5 fw-bold mb-3">
|
||||
Integrate AI with the <span class="hero-highlight">Symfony Framework</span>
|
||||
</h2>
|
||||
<p class="text-balance fs-5 mb-4 opacity-75">
|
||||
Configure agents, platforms, and stores with familiar YAML configuration.
|
||||
</p>
|
||||
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<a href="https://symfony.com/doc/current/ai/bundles/ai-bundle.html" class="btn btn-light btn-lg px-3 d-flex align-items-center">
|
||||
<span>Read the docs</span>
|
||||
{{ ux_icon('tabler:arrow-up-right', {width: 24, height: 24, class: 'ms-2'}) }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-6 mt-5 mt-xl-0">
|
||||
<pre class="terminal"><code><span class="comment"># config/packages/ai.yaml</span>
|
||||
<span class="keyword">ai</span>:
|
||||
<span class="keyword">platform</span>:
|
||||
<span class="keyword">openai</span>:
|
||||
<span class="keyword">api_key</span>: <span class="string">'%env(OPENAI_API_KEY)%'</span>
|
||||
<span class="keyword">agent</span>:
|
||||
<span class="keyword">blog</span>:
|
||||
<span class="keyword">model</span>: <span class="string">'gpt-4.1'</span>
|
||||
<span class="keyword">prompt</span>: <span class="string">'You are a helpful assistant ...'</span>
|
||||
<span class="keyword">tools</span>:
|
||||
- <span class="string">'App\Tool\SearchTool'</span>
|
||||
- <span class="title">agent</span>: <span class="string">'faq_knowledge'</span></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,398 +1,82 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block body %}
|
||||
<div class="hero-wrapper text-white">
|
||||
{{ include('_header.html.twig') }}
|
||||
|
||||
{{ include('_header.html.twig') }}
|
||||
<div class="hero-wrapper color-shift text-white" data-controller="hero-slider" data-hero-slider-interval-value="8000">
|
||||
<section class="hero-section">
|
||||
<div class="container">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-xl-6 pe-5">
|
||||
<h1 class="ff-title text-balance display-5 fw-bold mb-3">
|
||||
Build <span class="text-warning">AI capable</span>
|
||||
applications with PHP
|
||||
</h1>
|
||||
<p class="text-balance fs-5 mb-4">
|
||||
A set of components that integrate AI capabilities into PHP applications.
|
||||
</p>
|
||||
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<a href="https://symfony.com/doc/current/ai/bundles/ai-bundle.html" class="btn btn-light btn-lg px-3 d-flex align-items-center">
|
||||
<span>Add AI to your Symfony app</span>
|
||||
{{ ux_icon('tabler:arrow-up-right', {width: 24, height: 24, class: 'ms-2'}) }}
|
||||
</a>
|
||||
<a href="https://symfony.com/doc/current/ai/bundles/mcp-bundle.html" class="mt-3 mt-sm-0 ms-lg-3 btn btn-outline-light btn-lg px-3 d-flex align-items-center">
|
||||
Build MCP Servers
|
||||
{{ ux_icon('tabler:arrow-up-right', {width: 24, height: 24, class: 'ms-2'}) }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="hero-slides">
|
||||
<div class="hero-slide active" data-hero-slider-target="slide">
|
||||
{{ include('hero/_slide_agent.html.twig') }}
|
||||
</div>
|
||||
|
||||
<div class="col-xl-6 mt-5 mt-xl-0">
|
||||
{% set model_typed = stimulus_controller('typed', {
|
||||
loop: true,
|
||||
cursorChar: '|',
|
||||
strings: ['gpt-5.1', 'mistral-large-latest', 'gemini-3-pro-preview', 'claude-sonnet-4-5'],
|
||||
}) %}
|
||||
<pre class="terminal"><code><span class="variable">$platform</span> = <span class="title class_">PlatformFactory</span>::<span class="title function_ invoke__">create</span>(...);
|
||||
<span class="variable">$agent</span> = <span class="keyword">new</span> <span class="title class_">Agent</span>(<span class="variable">$platform</span>, <span class="string">'</span><span class="string" {{ model_typed|raw }}></span><span class="string">'</span>)
|
||||
|
||||
<span class="variable">$messages</span> = <span class="keyword">new</span> <span class="title class_">MessageBag</span>(
|
||||
<span class="title class_">Message</span>::<span class="title function_ invoke__">forSystem</span>(<span class="string">'You are a pirate and you write funny.'</span>),
|
||||
<span class="title class_">Message</span>::<span class="title function_ invoke__">ofUser</span>(<span class="string">'What is the Symfony framework?'</span>),
|
||||
);
|
||||
<span class="variable">$response</span> = <span class="variable">$agent</span>-><span class="title function_ invoke__">call</span>(<span class="variable">$messages</span>, [
|
||||
<span class="string">'temperature'</span> => 1.0, // specific options just for this call
|
||||
]);
|
||||
|
||||
<span class="keyword">echo</span> <span class="variable">$response</span>-><span class="title function_ invoke__">getContent</span>();</code></pre>
|
||||
<div class="hero-slide" data-hero-slider-target="slide">
|
||||
{{ include('hero/_slide_symfony.html.twig') }}
|
||||
</div>
|
||||
<div class="hero-slide" data-hero-slider-target="slide">
|
||||
{{ include('hero/_slide_mcp.html.twig') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hero-indicators">
|
||||
<button type="button" class="hero-indicator active" data-hero-slider-target="indicator" data-action="hero-slider#goToSlide" data-index="0" aria-label="Slide 1"></button>
|
||||
<button type="button" class="hero-indicator" data-hero-slider-target="indicator" data-action="hero-slider#goToSlide" data-index="1" aria-label="Slide 2"></button>
|
||||
<button type="button" class="hero-indicator" data-hero-slider-target="indicator" data-action="hero-slider#goToSlide" data-index="2" aria-label="Slide 3"></button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="hero-progress">
|
||||
<div class="hero-progress-bar" data-hero-slider-target="progress"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<main>
|
||||
<section class="py-5 py-lg-5">
|
||||
<div class="container">
|
||||
<div class="row g-4 gy-5 gy-sm-0">
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<h2 class="ff-title fw-bold h4 mb-4 text-center">Models</h2>
|
||||
<article class="sf-ai-card sf-ai-card-hover sf-ai-card-hover-dark p-3 p-lg-4">
|
||||
<div class="sf-ai-card-body sf-ai-logo-grid">
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:openai') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:google-gemini') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:mistral') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:claude') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<a href="https://symfony.com/doc/current/ai/components/platform.html#supported-models-platforms" class="mt-5 fw-semibold small text-decoration-none d-flex align-items-center justify-content-end">
|
||||
<span>See all models</span>
|
||||
{{ ux_icon('tabler:arrow-right', {width: 18, height: 18, class: 'ms-1'}) }}
|
||||
</a>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<h2 class="ff-title fw-bold h4 mb-4 mt-5 mt-lg-0 text-center">Platforms</h2>
|
||||
<article class="sf-ai-card sf-ai-card-hover sf-ai-card-hover-dark p-3 p-lg-4">
|
||||
<div class="sf-ai-card-body sf-ai-logo-grid">
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:hugging-face') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:azure') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:ollama') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:docker') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<a href="https://symfony.com/doc/current/ai/components/platform.html#supported-models-platforms" class="mt-5 fw-semibold small text-decoration-none d-flex align-items-center justify-content-end">
|
||||
<span>See all platforms</span>
|
||||
{{ ux_icon('tabler:arrow-right', {width: 18, height: 18, class: 'ms-1'}) }}
|
||||
</a>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<h2 class="ff-title fw-bold h4 mb-4 mt-5 mt-lg-0 text-center">Stores</h2>
|
||||
<article class="sf-ai-card sf-ai-card-hover sf-ai-card-hover-dark p-3 p-lg-4">
|
||||
<div class="sf-ai-card-body sf-ai-logo-grid">
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:pinecone') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:postgresql') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:mariadb') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:chroma') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<a href="https://symfony.com/doc/current/ai/components/store.html#supported-stores" class="mt-5 fw-semibold small text-decoration-none d-flex align-items-center justify-content-end">
|
||||
<span>See all stores</span>
|
||||
{{ ux_icon('tabler:arrow-right', {width: 18, height: 18, class: 'ms-1'}) }}
|
||||
</a>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<h2 class="ff-title fw-bold h4 mb-4 mt-5 mt-lg-0 text-center">Built-in Tools</h2>
|
||||
<article class="sf-ai-card sf-ai-card-hover sf-ai-card-hover-dark p-3 p-lg-4">
|
||||
<div class="sf-ai-card-body sf-ai-logo-grid">
|
||||
<span class="logo-pill">
|
||||
Similarity Search (RAG)
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:wikipedia') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:youtube') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:brave') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<a href="https://symfony.com/doc/current/ai/components/agent.html#code-examples-with-built-in-tools" class="mt-5 fw-semibold small text-decoration-none d-flex align-items-center justify-content-end">
|
||||
<span>See all tools</span>
|
||||
{{ ux_icon('tabler:arrow-right', {width: 18, height: 18, class: 'ms-1'}) }}
|
||||
</a>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="pb-5 mt-5">
|
||||
<div class="container">
|
||||
<div class="row mb-4">
|
||||
<div class="col-lg-8">
|
||||
<h2 class="ff-title fw-bold h3 mt-3 mb-1">Explore and try our demos</h2>
|
||||
</div>
|
||||
</div>
|
||||
{{ include('sections/_architecture.html.twig') }}
|
||||
<hr class="section-divider">
|
||||
{{ include('sections/_features.html.twig') }}
|
||||
<hr class="section-divider">
|
||||
{{ include('sections/_third-party.html.twig') }}
|
||||
<hr class="section-divider">
|
||||
{{ include('sections/_demos.html.twig') }}
|
||||
<hr class="section-divider">
|
||||
{{ include('sections/_mcp.html.twig') }}
|
||||
<hr class="section-divider">
|
||||
{{ include('sections/_mate.html.twig') }}
|
||||
|
||||
<section id="support" class="cta-section color-shift text-white py-4 position-relative overflow-hidden">
|
||||
<div class="cta-overlay"></div>
|
||||
<div class="container z-above">
|
||||
<h2 class="ff-title fw-bold text-center mb-4" data-aos="fade-up">Get Involved & Get Support</h2>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6">
|
||||
<article class="demo-youtube sf-ai-card sf-ai-card-hover h-100">
|
||||
<div class="position-relative card-body p-3 p-lg-4 d-flex gap-3 align-items-start">
|
||||
<div class="demo-icon bg-warning bg-opacity-10 text-warning flex-shrink-0">
|
||||
{{ ux_icon('tabler:brand-youtube', {width: 42, height: 42}) }}
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<div class="d-flex align-items-start gap-2 mb-1">
|
||||
<h3 class="h5 ff-title fw-bold mb-0">
|
||||
<a href="https://github.com/symfony/ai-demo" class="stretched-link">Youtube Transcript Bot</a>
|
||||
</h3>
|
||||
{{ ux_icon('tabler:arrow-right', {width: 24, height: 24, class: 'ms-1'}) }}
|
||||
</div>
|
||||
<p class="text-balance mb-0 text-muted small">
|
||||
Question answering started with a YouTube video ID which gets converted
|
||||
into a transcript.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<div class="col-md-4" data-aos="fade-up" data-aos-delay="0">
|
||||
<a href="https://symfony.com/doc/current/ai/index.html" target="_blank" rel="noopener noreferrer" class="cta-card d-block text-white text-decoration-none h-100 p-4 text-center">
|
||||
{{ ux_icon('tabler:book', {width: 36, height: 36, class: 'mb-3 opacity-75'}) }}
|
||||
<h3 class="h5 ff-title fw-bold mb-2">Documentation</h3>
|
||||
<p class="mb-0 small opacity-75">
|
||||
Get going with the official Symfony AI docs
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<article class="demo-recipe sf-ai-card sf-ai-card-hover h-100">
|
||||
<div class="position-relative card-body p-3 p-lg-4 d-flex gap-3 align-items-start">
|
||||
<div class="demo-icon bg-warning bg-opacity-10 text-warning flex-shrink-0">
|
||||
{{ ux_icon('mdi:cook', {width: 42, height: 42}) }}
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<div class="d-flex align-items-start gap-2 mb-1">
|
||||
<h3 class="h5 ff-title fw-bold mb-0">
|
||||
<a href="https://github.com/symfony/ai-demo" class="stretched-link">Recipe Bot</a>
|
||||
</h3>
|
||||
{{ ux_icon('tabler:arrow-right', {width: 24, height: 24, class: 'ms-1'}) }}
|
||||
</div>
|
||||
<p class="text-balance mb-0 text-muted small">
|
||||
Chatbot for proposing cooking recipes - powered by structured output.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<div class="col-md-4" data-aos="fade-up" data-aos-delay="100">
|
||||
<a href="https://github.com/symfony/ai/issues" target="_blank" rel="noopener noreferrer" class="cta-card d-block text-white text-decoration-none h-100 p-4 text-center">
|
||||
{{ ux_icon('simple-icons:github', {width: 36, height: 36, class: 'mb-3 opacity-75'}) }}
|
||||
<h3 class="h5 ff-title fw-bold mb-2">Community</h3>
|
||||
<p class="mb-0 small opacity-75">
|
||||
Feedback, ideas, contribution
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<article class="demo-wikipedia sf-ai-card sf-ai-card-hover h-100">
|
||||
<div class="position-relative card-body p-3 p-lg-4 d-flex gap-3 align-items-start">
|
||||
<div class="demo-icon bg-success bg-opacity-10 text-success flex-shrink-0">
|
||||
{{ ux_icon('simple-icons:wikipedia', {width: 42, height: 42}) }}
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<div class="d-flex align-items-start gap-2 mb-1">
|
||||
<h3 class="h5 ff-title fw-bold mb-0">
|
||||
<a href="https://github.com/symfony/ai-demo" class="stretched-link">Wikipedia research Bot</a>
|
||||
</h3>
|
||||
{{ ux_icon('tabler:arrow-right', {width: 24, height: 24, class: 'ms-1'}) }}
|
||||
</div>
|
||||
<p class="text-balance mb-0 text-muted small">
|
||||
A chatbot equipped with tools to search and read on Wikipedia about
|
||||
topics the users ask for.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<article class="demo-blog sf-ai-card sf-ai-card-hover h-100">
|
||||
<div class="position-relative card-body p-3 p-lg-4 d-flex gap-3 align-items-start">
|
||||
<div class="demo-icon bg-primary bg-opacity-10 text-primary flex-shrink-0">
|
||||
{{ ux_icon('logos:symfony-letters', {width: 36, height: 36}) }}
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<div class="d-flex align-items-start gap-2 mb-1">
|
||||
<h3 class="h5 ff-title fw-bold mb-0">
|
||||
<a href="https://github.com/symfony/ai-demo" class="stretched-link">Symfony Blog Bot</a>
|
||||
</h3>
|
||||
{{ ux_icon('tabler:arrow-right', {width: 24, height: 24, class: 'ms-1'}) }}
|
||||
</div>
|
||||
<p class="text-balance mb-0 text-muted small">
|
||||
Retrieval Augmented Generation (RAG) based on Symfony's blog dumped to a
|
||||
vector store.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<article class="demo-speech sf-ai-card sf-ai-card-hover h-100">
|
||||
<div class="position-relative card-body p-3 p-lg-4 d-flex gap-3 align-items-start">
|
||||
<div class="demo-icon bg-success bg-opacity-10 text-success flex-shrink-0">
|
||||
{{ ux_icon('tabler:microphone', {width: 42, height: 42}) }}
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<div class="d-flex align-items-start gap-2 mb-1">
|
||||
<h3 class="h5 ff-title fw-bold mb-0">
|
||||
<a href="https://github.com/symfony/ai-demo" class="stretched-link">Speech Bot + Subagent</a>
|
||||
</h3>
|
||||
{{ ux_icon('tabler:arrow-right', {width: 24, height: 24, class: 'ms-1'}) }}
|
||||
</div>
|
||||
<p class="text-balance mb-0 text-muted small">
|
||||
Demonstration of speech-to-text & text-to-speech and a subagent, combining 4
|
||||
models in total.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<article class="demo-video sf-ai-card sf-ai-card-hover h-100">
|
||||
<div class="position-relative card-body p-3 p-lg-4 d-flex gap-3 align-items-start">
|
||||
<div class="demo-icon bg-info bg-opacity-10 text-info flex-shrink-0">
|
||||
{{ ux_icon('tabler:video', {width: 42, height: 42}) }}
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<div class="d-flex align-items-start gap-2 mb-1">
|
||||
<h3 class="h5 ff-title fw-bold mb-0">
|
||||
<a href="https://github.com/symfony/ai-demo" class="stretched-link">Video Bot</a>
|
||||
</h3>
|
||||
{{ ux_icon('tabler:arrow-right', {width: 24, height: 24, class: 'ms-1'}) }}
|
||||
</div>
|
||||
<p class="text-balance mb-0 text-muted small">
|
||||
Demonstration of vision capabilities of GPT in combination with your webcam.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<article class="demo-crop sf-ai-card sf-ai-card-hover h-100">
|
||||
<div class="position-relative card-body p-3 p-lg-4 d-flex gap-3 align-items-start">
|
||||
<div class="demo-icon bg-success bg-opacity-10 text-success flex-shrink-0">
|
||||
{{ ux_icon('tabler:crop', {width: 42, height: 42}) }}
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<div class="d-flex align-items-start gap-2 mb-1">
|
||||
<h3 class="h5 ff-title fw-bold mb-0">
|
||||
<a href="https://github.com/symfony/ai-demo" class="stretched-link">Smart Image Cropping</a>
|
||||
</h3>
|
||||
{{ ux_icon('tabler:arrow-right', {width: 24, height: 24, class: 'ms-1'}) }}
|
||||
</div>
|
||||
<p class="text-balance mb-0 text-muted small">
|
||||
AI-assisted image cropping to focus on key elements on the image while resizing.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<article class="demo-turbo sf-ai-card sf-ai-card-hover h-100">
|
||||
<div class="position-relative card-body p-3 p-lg-4 d-flex gap-3 align-items-start">
|
||||
<div class="demo-icon bg-primary bg-opacity-10 text-primary flex-shrink-0">
|
||||
{{ ux_icon('logos:symfony-letters', {width: 36, height: 36}) }}
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<div class="d-flex align-items-start gap-2 mb-1">
|
||||
<h3 class="h5 ff-title fw-bold mb-0">
|
||||
<a href="https://github.com/symfony/ai-demo" class="stretched-link">Turbo Stream Bot</a>
|
||||
</h3>
|
||||
{{ ux_icon('tabler:arrow-right', {width: 24, height: 24, class: 'ms-1'}) }}
|
||||
</div>
|
||||
<p class="text-balance mb-0 text-muted small">
|
||||
Demonstration of streaming text responses in combination with markdown based on
|
||||
Turbo and SSE.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-secondary mt-5 py-5">
|
||||
<div class="container">
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4">
|
||||
<article class="sf-ai-card sf-ai-card-hover h-100">
|
||||
<div class="card-body p-3 p-lg-4 position-relative">
|
||||
<div class="d-flex align-items-start justify-content-between gap-2 mb-1">
|
||||
<h3 class="h5 ff-title fw-bold mb-3">
|
||||
<a class="stretched-link" href="https://symfony.com/doc/current/ai/index.html" target="_blank">Documentation</a>
|
||||
</h3>
|
||||
{{ ux_icon('tabler:book', {width: 24, height: 24, class: 'ms-1 text-muted'}) }}
|
||||
</div>
|
||||
<p class="text-muted small mb-0">
|
||||
Get going with the official Symfony AI docs
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<article class="sf-ai-card sf-ai-card-hover h-100">
|
||||
<div class="card-body p-3 p-lg-4 position-relative d-flex flex-column">
|
||||
<div class="flex-grow-1 d-flex align-items-start justify-content-between gap-2 mb-1">
|
||||
<h3 class="h5 ff-title fw-bold mb-3">
|
||||
<a class="stretched-link" href="https://github.com/symfony/ai/issues" target="_blank">Community</a>
|
||||
</h3>
|
||||
{{ ux_icon('simple-icons:github', {width: 24, height: 24, class: 'ms-1 text-muted'}) }}
|
||||
</div>
|
||||
<p class="text-muted small mb-0">
|
||||
Feedback, ideas, contribution
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<article class="sf-ai-card sf-ai-card-hover h-100">
|
||||
<div class="card-body p-3 p-lg-4 position-relative">
|
||||
<div class="d-flex align-items-start justify-content-between gap-2 mb-1">
|
||||
<h3 class="h5 ff-title fw-bold mb-3">
|
||||
<a class="stretched-link" href="https://symfony-devs.slack.com/archives/C09BAPA9BJP" target="_blank">Support in Slack</a>
|
||||
</h3>
|
||||
{{ ux_icon('tabler:brand-slack', {width: 24, height: 24, class: 'ms-1 text-muted'}) }}
|
||||
</div>
|
||||
<p class="text-muted small mb-0">
|
||||
Ask questions in Slack channel <strong>#ai-initiative</strong>
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
<div class="col-md-4" data-aos="fade-up" data-aos-delay="200">
|
||||
<a href="https://symfony-devs.slack.com/archives/C09BAPA9BJP" target="_blank" rel="noopener noreferrer" class="cta-card d-block text-white text-decoration-none h-100 p-4 text-center">
|
||||
{{ ux_icon('tabler:brand-slack', {width: 36, height: 36, class: 'mb-3 opacity-75'}) }}
|
||||
<h3 class="h5 ff-title fw-bold mb-2">Support in Slack</h3>
|
||||
<p class="mb-0 small opacity-75">
|
||||
Ask questions in channel <strong>#ai-initiative</strong>
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
98
ai.symfony.com/templates/sections/_architecture.html.twig
Normal file
98
ai.symfony.com/templates/sections/_architecture.html.twig
Normal file
@@ -0,0 +1,98 @@
|
||||
<section id="components" class="py-5 py-lg-5 section-gradient-mesh">
|
||||
<div class="container z-above">
|
||||
<div class="row align-items-center g-5">
|
||||
<div class="col-lg-5" data-aos="fade-right">
|
||||
<h2 class="ff-title fw-bold h3 mb-3 section-heading">Component Architecture</h2>
|
||||
<p class="text-muted mb-4">
|
||||
A set of decoupled PHP components for AI integrations. Each works independently
|
||||
without the full framework. The foundation layer abstracts away third-party specifics.
|
||||
</p>
|
||||
|
||||
<div class="d-flex flex-column gap-1">
|
||||
<a href="https://symfony.com/doc/current/ai/components/platform.html" class="component-link">
|
||||
<span class="component-dot component-dot-platform"></span>
|
||||
<div>
|
||||
<strong class="ff-title">Platform</strong>
|
||||
<span class="text-muted small d-block">Unified interface to AI providers</span>
|
||||
</div>
|
||||
</a>
|
||||
<a href="https://symfony.com/doc/current/ai/components/store.html" class="component-link">
|
||||
<span class="component-dot component-dot-store"></span>
|
||||
<div>
|
||||
<strong class="ff-title">Store</strong>
|
||||
<span class="text-muted small d-block">Vector database abstraction</span>
|
||||
</div>
|
||||
</a>
|
||||
<a href="https://symfony.com/doc/current/ai/components/agent.html" class="component-link">
|
||||
<span class="component-dot component-dot-agent"></span>
|
||||
<div>
|
||||
<strong class="ff-title">Agent</strong>
|
||||
<span class="text-muted small d-block">Framework for AI agents with tools</span>
|
||||
</div>
|
||||
</a>
|
||||
<a href="https://symfony.com/doc/current/ai/bundles/ai-bundle.html" class="component-link">
|
||||
<span class="component-dot component-dot-bundle"></span>
|
||||
<div>
|
||||
<strong class="ff-title">AI Bundle</strong>
|
||||
<span class="text-muted small d-block">Symfony framework integration</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-7" data-aos="fade-left">
|
||||
<div class="architecture-diagram">
|
||||
<svg viewBox="4 4 512 372" xmlns="http://www.w3.org/2000/svg" width="100%" role="img" aria-label="Symfony AI component architecture diagram showing four layers: third-party integrations, foundation, agent, and AI bundle">
|
||||
<defs>
|
||||
<style>
|
||||
.arch-label { font-family: var(--font-family-title), system-ui, sans-serif; font-weight: 700; font-size: 15px; fill: var(--sf-ai-text-color); }
|
||||
.arch-label-white { fill: #fff; }
|
||||
.arch-sublabel { font-family: system-ui, sans-serif; font-size: 11px; font-weight: 500; fill: var(--sf-ai-text-color); opacity: 0.5; letter-spacing: 0.05em; text-transform: uppercase; }
|
||||
.arch-item-text { font-family: var(--font-family-title), system-ui, sans-serif; font-size: 12px; font-weight: 600; fill: var(--sf-ai-text-color); }
|
||||
</style>
|
||||
<filter id="layerShadow" x="-2%" y="-2%" width="104%" height="110%">
|
||||
<feDropShadow dx="0" dy="2" stdDeviation="4" flood-color="rgba(0,0,0,0.08)"/>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
{# Layer 4: AI Bundle (top) #}
|
||||
<rect class="arch-shift-strong" x="10" y="14" width="500" height="70" rx="14" ry="14" fill="var(--sf-ai-brand-1)" filter="url(#layerShadow)"/>
|
||||
<text class="arch-label arch-label-white" x="260" y="45" text-anchor="middle" font-size="17">AI Bundle</text>
|
||||
<text x="260" y="65" text-anchor="middle" fill="rgba(255,255,255,0.65)" font-family="system-ui, sans-serif" font-size="12" font-weight="500">Symfony Framework Integration</text>
|
||||
|
||||
{# Connector: AI Bundle → Agent #}
|
||||
<line x1="260" y1="84" x2="260" y2="104" stroke="var(--sf-ai-text-color)" stroke-opacity="0.2" stroke-width="2"/>
|
||||
|
||||
{# Layer 3: Agent #}
|
||||
<rect class="arch-shift-medium" x="10" y="104" width="500" height="64" rx="14" ry="14" fill="var(--sf-ai-brand-1)"/>
|
||||
<text class="arch-label" x="260" y="142" text-anchor="middle" fill="#fff">Agent</text>
|
||||
|
||||
{# Connector: Agent → Foundation #}
|
||||
<line x1="260" y1="168" x2="260" y2="188" stroke="var(--sf-ai-text-color)" stroke-opacity="0.2" stroke-width="2"/>
|
||||
|
||||
{# Layer 2: Foundation — 3 equal-width columns #}
|
||||
<rect class="arch-shift-light" x="10" y="188" width="500" height="80" rx="14" ry="14" fill="var(--sf-ai-brand-1)"/>
|
||||
<text class="arch-sublabel" x="260" y="210" text-anchor="middle" fill="rgba(255,255,255,0.7)">Foundation Layer</text>
|
||||
<line x1="172" y1="218" x2="172" y2="258" stroke="rgba(255,255,255,0.25)" stroke-width="1"/>
|
||||
<line x1="338" y1="218" x2="338" y2="258" stroke="rgba(255,255,255,0.25)" stroke-width="1"/>
|
||||
<text class="arch-item-text" x="92" y="244" text-anchor="middle" fill="#fff">Platform</text>
|
||||
<text class="arch-item-text" x="255" y="244" text-anchor="middle" fill="#fff">Store</text>
|
||||
<text class="arch-item-text" x="420" y="244" text-anchor="middle" fill="#fff">Tools</text>
|
||||
|
||||
{# Connector: Foundation → 3rd Party #}
|
||||
<line x1="260" y1="268" x2="260" y2="288" stroke="var(--sf-ai-text-color)" stroke-opacity="0.2" stroke-width="2"/>
|
||||
|
||||
{# Layer 1: 3rd Party Integrations (bottom) — 3 equal-width columns #}
|
||||
<rect class="arch-shift-muted" x="10" y="288" width="500" height="80" rx="14" ry="14" fill="var(--sf-ai-brand-1)"/>
|
||||
<text class="arch-sublabel" x="260" y="310" text-anchor="middle" fill="rgba(255,255,255,0.7)">3rd Party Integrations</text>
|
||||
<line x1="172" y1="318" x2="172" y2="358" stroke="rgba(255,255,255,0.6)" stroke-width="1"/>
|
||||
<line x1="338" y1="318" x2="338" y2="358" stroke="rgba(255,255,255,0.6)" stroke-width="1"/>
|
||||
<text class="arch-item-text" x="92" y="344" text-anchor="middle" fill="#fff">Platform Bridges</text>
|
||||
<text class="arch-item-text" x="255" y="344" text-anchor="middle" fill="#fff">Store Bridges</text>
|
||||
<text class="arch-item-text" x="420" y="344" text-anchor="middle" fill="#fff">Agent Tools</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
132
ai.symfony.com/templates/sections/_demos.html.twig
Normal file
132
ai.symfony.com/templates/sections/_demos.html.twig
Normal file
@@ -0,0 +1,132 @@
|
||||
<section id="demos" class="section-secondary py-5 py-lg-5">
|
||||
<div class="container">
|
||||
<div class="row mb-4">
|
||||
<div class="col-lg-8">
|
||||
<h2 class="ff-title fw-bold h3 mb-1 section-heading" data-aos="fade-up">Demos</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sf-ai-card p-4 p-lg-5" data-controller="clipboard" data-aos="fade-up" data-aos-delay="100">
|
||||
<div class="row align-items-center g-4">
|
||||
<div class="col-lg-5">
|
||||
<h3 class="ff-title fw-bold h5 mb-3">Get started in seconds</h3>
|
||||
<p class="text-muted small mb-0">
|
||||
Clone the demo, add your OpenAI API key, and start the server.
|
||||
The app includes a chatbot UI, RAG pipeline, and multiple demo scenarios ready to explore.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-lg-7 position-relative">
|
||||
<button type="button" class="copy-btn" data-action="clipboard#copy" data-clipboard-target="button" aria-label="Copy to clipboard" data-bs-toggle="tooltip" data-bs-placement="top" title="Copy to clipboard">
|
||||
{{ ux_icon('tabler:copy', {width: 16, height: 16}) }}
|
||||
</button>
|
||||
<pre class="terminal mb-0" data-clipboard-target="source"><code>$ composer create-project symfony/ai-demo
|
||||
$ cd ai-demo
|
||||
$ echo "OPENAI_API_KEY='sk-...'" > .env.local
|
||||
$ docker compose up -d
|
||||
$ symfony serve -d</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4 opacity-10">
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="d-flex gap-2 align-items-start">
|
||||
<div class="demo-icon-sm demo-youtube flex-shrink-0">
|
||||
{{ ux_icon('tabler:brand-youtube', {width: 22, height: 22}) }}
|
||||
</div>
|
||||
<div>
|
||||
<strong class="ff-title small d-block">YouTube Transcript Bot</strong>
|
||||
<span class="text-muted demo-desc">Question answering from YouTube video transcripts.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="d-flex gap-2 align-items-start">
|
||||
<div class="demo-icon-sm demo-recipe flex-shrink-0">
|
||||
{{ ux_icon('mdi:cook', {width: 22, height: 22}) }}
|
||||
</div>
|
||||
<div>
|
||||
<strong class="ff-title small d-block">Recipe Bot</strong>
|
||||
<span class="text-muted demo-desc">Cooking recipe chatbot powered by structured output.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="d-flex gap-2 align-items-start">
|
||||
<div class="demo-icon-sm demo-wikipedia flex-shrink-0">
|
||||
{{ ux_icon('simple-icons:wikipedia', {width: 22, height: 22}) }}
|
||||
</div>
|
||||
<div>
|
||||
<strong class="ff-title small d-block">Wikipedia Research Bot</strong>
|
||||
<span class="text-muted demo-desc">Search and read Wikipedia with AI-powered tools.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="d-flex gap-2 align-items-start">
|
||||
<div class="demo-icon-sm demo-blog flex-shrink-0">
|
||||
{{ ux_icon('logos:symfony-letters', {width: 18, height: 18}) }}
|
||||
</div>
|
||||
<div>
|
||||
<strong class="ff-title small d-block">Symfony Blog Bot</strong>
|
||||
<span class="text-muted demo-desc">RAG based on Symfony's blog dumped to a vector store.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="d-flex gap-2 align-items-start">
|
||||
<div class="demo-icon-sm demo-speech flex-shrink-0">
|
||||
{{ ux_icon('tabler:microphone', {width: 22, height: 22}) }}
|
||||
</div>
|
||||
<div>
|
||||
<strong class="ff-title small d-block">Speech Bot + Subagent</strong>
|
||||
<span class="text-muted demo-desc">Speech-to-text, text-to-speech, and subagent combining 4 models.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="d-flex gap-2 align-items-start">
|
||||
<div class="demo-icon-sm demo-video flex-shrink-0">
|
||||
{{ ux_icon('tabler:video', {width: 22, height: 22}) }}
|
||||
</div>
|
||||
<div>
|
||||
<strong class="ff-title small d-block">Video Bot</strong>
|
||||
<span class="text-muted demo-desc">Vision capabilities of GPT in combination with your webcam.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="d-flex gap-2 align-items-start">
|
||||
<div class="demo-icon-sm demo-crop flex-shrink-0">
|
||||
{{ ux_icon('tabler:crop', {width: 22, height: 22}) }}
|
||||
</div>
|
||||
<div>
|
||||
<strong class="ff-title small d-block">Smart Image Cropping</strong>
|
||||
<span class="text-muted demo-desc">AI-assisted cropping to focus on key elements while resizing.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="d-flex gap-2 align-items-start">
|
||||
<div class="demo-icon-sm demo-turbo flex-shrink-0">
|
||||
{{ ux_icon('logos:symfony-letters', {width: 18, height: 18}) }}
|
||||
</div>
|
||||
<div>
|
||||
<strong class="ff-title small d-block">Turbo Stream Bot</strong>
|
||||
<span class="text-muted demo-desc">Streaming text responses with markdown via Turbo and SSE.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-4">
|
||||
<a href="https://github.com/symfony/ai-demo" class="btn btn-outline-secondary px-4 d-inline-flex align-items-center">
|
||||
{{ ux_icon('simple-icons:github', {width: 18, height: 18, class: 'me-2'}) }}
|
||||
<span>Explore the Demo Repository</span>
|
||||
{{ ux_icon('tabler:arrow-up-right', {width: 18, height: 18, class: 'ms-2'}) }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
299
ai.symfony.com/templates/sections/_features.html.twig
Normal file
299
ai.symfony.com/templates/sections/_features.html.twig
Normal file
@@ -0,0 +1,299 @@
|
||||
<section id="features" class="section-secondary py-5 py-lg-5" data-controller="feature-tabs">
|
||||
<div class="container">
|
||||
<h2 class="ff-title fw-bold h3 mb-4 text-center section-heading" data-aos="fade-up">Features</h2>
|
||||
|
||||
<div class="d-flex flex-wrap justify-content-center gap-2 mb-4" data-aos="fade-up" data-aos-delay="100">
|
||||
<button type="button" class="feature-tab active" data-feature-tabs-target="tab" data-action="feature-tabs#switch" data-index="0">Model Inference</button>
|
||||
<button type="button" class="feature-tab" data-feature-tabs-target="tab" data-action="feature-tabs#switch" data-index="1">Streaming</button>
|
||||
<button type="button" class="feature-tab" data-feature-tabs-target="tab" data-action="feature-tabs#switch" data-index="2">Speech</button>
|
||||
<button type="button" class="feature-tab" data-feature-tabs-target="tab" data-action="feature-tabs#switch" data-index="3">Multi-Modal</button>
|
||||
<button type="button" class="feature-tab" data-feature-tabs-target="tab" data-action="feature-tabs#switch" data-index="4">Structured Output</button>
|
||||
<button type="button" class="feature-tab" data-feature-tabs-target="tab" data-action="feature-tabs#switch" data-index="5">Agents</button>
|
||||
<button type="button" class="feature-tab" data-feature-tabs-target="tab" data-action="feature-tabs#switch" data-index="6">Token Usage</button>
|
||||
<button type="button" class="feature-tab" data-feature-tabs-target="tab" data-action="feature-tabs#switch" data-index="7">Tool Calling</button>
|
||||
<button type="button" class="feature-tab" data-feature-tabs-target="tab" data-action="feature-tabs#switch" data-index="8">Subagents</button>
|
||||
<button type="button" class="feature-tab" data-feature-tabs-target="tab" data-action="feature-tabs#switch" data-index="9">RAG</button>
|
||||
</div>
|
||||
|
||||
{# Model Inference #}
|
||||
<div class="feature-panel active" data-feature-tabs-target="panel">
|
||||
<div class="row align-items-center g-4">
|
||||
<div class="col-lg-5" data-aos="fade-right">
|
||||
<h3 class="ff-title fw-bold h4 mb-3">Model Inference</h3>
|
||||
<p class="text-muted mb-4">
|
||||
Send prompts to any AI model and get responses with a single method call.
|
||||
The Platform abstracts away provider differences — switch between OpenAI, Anthropic, Mistral, or local models without changing your application code.
|
||||
</p>
|
||||
<a href="https://github.com/symfony/ai/blob/main/examples/openai/chat.php" class="btn btn-dark px-3 d-inline-flex align-items-center" target="_blank" rel="noopener noreferrer">
|
||||
{{ ux_icon('simple-icons:github', {width: 18, height: 18, class: 'me-2'}) }}
|
||||
<span>See example</span>
|
||||
{{ ux_icon('tabler:arrow-up-right', {width: 18, height: 18, class: 'ms-2'}) }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-lg-7" data-aos="fade-left">
|
||||
<pre class="terminal"><code><span class="variable">$platform</span> = <span class="title class_">PlatformFactory</span>::<span class="title function_ invoke__">create</span>(<span class="variable">$apiKey</span>, <span class="title function_ invoke__">http_client</span>());
|
||||
|
||||
<span class="variable">$messages</span> = <span class="keyword">new</span> <span class="title class_">MessageBag</span>(
|
||||
<span class="title class_">Message</span>::<span class="title function_ invoke__">forSystem</span>(<span class="string">'You are a helpful assistant.'</span>),
|
||||
<span class="title class_">Message</span>::<span class="title function_ invoke__">ofUser</span>(<span class="string">'What is the Symfony framework?'</span>),
|
||||
);
|
||||
|
||||
<span class="variable">$result</span> = <span class="variable">$platform</span>-><span class="title function_ invoke__">invoke</span>(<span class="string">'gpt-5-mini'</span>, <span class="variable">$messages</span>);
|
||||
<span class="keyword">echo</span> <span class="variable">$result</span>-><span class="title function_ invoke__">asText</span>();</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Streaming #}
|
||||
<div class="feature-panel" data-feature-tabs-target="panel">
|
||||
<div class="row align-items-center g-4">
|
||||
<div class="col-lg-5">
|
||||
<h3 class="ff-title fw-bold h4 mb-3">Streaming</h3>
|
||||
<p class="text-muted mb-4">
|
||||
Stream responses token by token for real-time output.
|
||||
Instead of waiting for the full response, display text as it's generated — just like ChatGPT does. Works with any supported model and platform.
|
||||
</p>
|
||||
<a href="https://github.com/symfony/ai/blob/main/examples/openai/stream.php" class="btn btn-dark px-3 d-inline-flex align-items-center" target="_blank" rel="noopener noreferrer">
|
||||
{{ ux_icon('simple-icons:github', {width: 18, height: 18, class: 'me-2'}) }}
|
||||
<span>See example</span>
|
||||
{{ ux_icon('tabler:arrow-up-right', {width: 18, height: 18, class: 'ms-2'}) }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<pre class="terminal"><code><span class="variable">$result</span> = <span class="variable">$platform</span>-><span class="title function_ invoke__">invoke</span>(<span class="string">'gpt-5-mini'</span>, <span class="variable">$messages</span>, [
|
||||
<span class="string">'stream'</span> => <span class="keyword">true</span>,
|
||||
]);
|
||||
|
||||
<span class="keyword">foreach</span> (<span class="variable">$result</span>-><span class="title function_ invoke__">asStream</span>() <span class="keyword">as</span> <span class="variable">$chunk</span>) {
|
||||
<span class="keyword">echo</span> <span class="variable">$chunk</span>; <span class="comment">// process chunks as they arrive</span>
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Speech #}
|
||||
<div class="feature-panel" data-feature-tabs-target="panel">
|
||||
<div class="row align-items-center g-4">
|
||||
<div class="col-lg-5">
|
||||
<h3 class="ff-title fw-bold h4 mb-3">Speech</h3>
|
||||
<p class="text-muted mb-4">
|
||||
Convert text to natural-sounding speech or transcribe audio recordings to text.
|
||||
Build voice assistants, podcast transcription tools, or accessibility features with providers like ElevenLabs or OpenAI Whisper — all through the same interface.
|
||||
</p>
|
||||
<a href="https://github.com/symfony/ai/blob/main/examples/elevenlabs/text-to-speech.php" class="btn btn-dark px-3 d-inline-flex align-items-center" target="_blank" rel="noopener noreferrer">
|
||||
{{ ux_icon('simple-icons:github', {width: 18, height: 18, class: 'me-2'}) }}
|
||||
<span>See example</span>
|
||||
{{ ux_icon('tabler:arrow-up-right', {width: 18, height: 18, class: 'ms-2'}) }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<pre class="terminal"><code><span class="comment">// Text to Speech</span>
|
||||
<span class="variable">$result</span> = <span class="variable">$platform</span>-><span class="title function_ invoke__">invoke</span>(<span class="string">'eleven_multilingual_v2'</span>, <span class="keyword">new</span> <span class="title class_">Text</span>(<span class="string">'Hello!'</span>), [
|
||||
<span class="string">'voice'</span> => <span class="string">'pqHfZKP75CvOlQylNhV4'</span>,
|
||||
]);
|
||||
<span class="variable">$result</span>-><span class="title function_ invoke__">asFile</span>(<span class="string">'output.mp3'</span>);
|
||||
|
||||
<span class="comment">// Speech to Text</span>
|
||||
<span class="variable">$result</span> = <span class="variable">$platform</span>-><span class="title function_ invoke__">invoke</span>(<span class="string">'scribe_v1'</span>, <span class="title class_">Audio</span>::<span class="title function_ invoke__">fromFile</span>(<span class="string">'recording.mp3'</span>));
|
||||
<span class="keyword">echo</span> <span class="variable">$result</span>-><span class="title function_ invoke__">asText</span>();</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Multi-Modal #}
|
||||
<div class="feature-panel" data-feature-tabs-target="panel">
|
||||
<div class="row align-items-center g-4">
|
||||
<div class="col-lg-5">
|
||||
<h3 class="ff-title fw-bold h4 mb-3">Multi-Modal</h3>
|
||||
<p class="text-muted mb-4">
|
||||
Send images, PDFs, and audio alongside text to any supported model.
|
||||
Build apps that can analyze photos, extract data from documents, or process
|
||||
voice recordings — all through the same simple API.
|
||||
</p>
|
||||
<a href="https://github.com/symfony/ai/blob/main/examples/openai/image-input-binary.php" class="btn btn-dark px-3 d-inline-flex align-items-center" target="_blank" rel="noopener noreferrer">
|
||||
{{ ux_icon('simple-icons:github', {width: 18, height: 18, class: 'me-2'}) }}
|
||||
<span>See example</span>
|
||||
{{ ux_icon('tabler:arrow-up-right', {width: 18, height: 18, class: 'ms-2'}) }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<pre class="terminal"><code><span class="variable">$messages</span> = <span class="keyword">new</span> <span class="title class_">MessageBag</span>(
|
||||
<span class="title class_">Message</span>::<span class="title function_ invoke__">ofUser</span>(
|
||||
<span class="string">'Describe this image'</span>,
|
||||
<span class="title class_">Image</span>::<span class="title function_ invoke__">fromFile</span>(<span class="string">'/path/to/photo.jpg'</span>),
|
||||
),
|
||||
);
|
||||
<span class="variable">$result</span> = <span class="variable">$platform</span>-><span class="title function_ invoke__">invoke</span>(<span class="string">'gpt-5-mini'</span>, <span class="variable">$messages</span>);
|
||||
|
||||
<span class="keyword">echo</span> <span class="variable">$result</span>-><span class="title function_ invoke__">asText</span>();</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Structured Output #}
|
||||
<div class="feature-panel" data-feature-tabs-target="panel">
|
||||
<div class="row align-items-center g-4">
|
||||
<div class="col-lg-5">
|
||||
<h3 class="ff-title fw-bold h4 mb-3">Structured Output</h3>
|
||||
<p class="text-muted mb-4">
|
||||
Map AI responses directly to PHP objects with automatic deserialization.
|
||||
Instead of parsing JSON strings yourself, get type-safe PHP objects back from the model — ready to use in your application logic.
|
||||
</p>
|
||||
<a href="https://github.com/symfony/ai/blob/main/examples/openai/structured-output-math.php" class="btn btn-dark px-3 d-inline-flex align-items-center" target="_blank" rel="noopener noreferrer">
|
||||
{{ ux_icon('simple-icons:github', {width: 18, height: 18, class: 'me-2'}) }}
|
||||
<span>See example</span>
|
||||
{{ ux_icon('tabler:arrow-up-right', {width: 18, height: 18, class: 'ms-2'}) }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<pre class="terminal"><code><span class="variable">$messages</span> = <span class="keyword">new</span> <span class="title class_">MessageBag</span>(
|
||||
<span class="title class_">Message</span>::<span class="title function_ invoke__">forSystem</span>(<span class="string">'You are a helpful math tutor.'</span>),
|
||||
<span class="title class_">Message</span>::<span class="title function_ invoke__">ofUser</span>(<span class="string">'Solve 8x + 7 = -23'</span>),
|
||||
);
|
||||
|
||||
<span class="variable">$result</span> = <span class="variable">$platform</span>-><span class="title function_ invoke__">invoke</span>(<span class="string">'gpt-5-mini'</span>, <span class="variable">$messages</span>, [
|
||||
<span class="string">'response_format'</span> => <span class="title class_">MathReasoning</span>::<span class="keyword">class</span>,
|
||||
]);
|
||||
|
||||
<span class="variable">$math</span> = <span class="variable">$result</span>-><span class="title function_ invoke__">asObject</span>(); <span class="comment">// MathReasoning instance</span></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Agents #}
|
||||
<div class="feature-panel" data-feature-tabs-target="panel">
|
||||
<div class="row align-items-center g-4">
|
||||
<div class="col-lg-5">
|
||||
<h3 class="ff-title fw-bold h4 mb-3">Agents</h3>
|
||||
<p class="text-muted mb-4">
|
||||
Build autonomous agents that reason, plan, and execute multi-step tasks.
|
||||
An Agent wraps a model with tools and processors, handling the full loop of calling tools, reading results, and deciding what to do next — until the task is complete.
|
||||
</p>
|
||||
<a href="https://github.com/symfony/ai/blob/main/examples/openai/agent.php" class="btn btn-dark px-3 d-inline-flex align-items-center" target="_blank" rel="noopener noreferrer">
|
||||
{{ ux_icon('simple-icons:github', {width: 18, height: 18, class: 'me-2'}) }}
|
||||
<span>See example</span>
|
||||
{{ ux_icon('tabler:arrow-up-right', {width: 18, height: 18, class: 'ms-2'}) }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<pre class="terminal"><code><span class="variable">$agent</span> = <span class="keyword">new</span> <span class="title class_">Agent</span>(<span class="variable">$platform</span>, <span class="string">'gpt-5-mini'</span>);
|
||||
|
||||
<span class="variable">$messages</span> = <span class="keyword">new</span> <span class="title class_">MessageBag</span>(
|
||||
<span class="title class_">Message</span>::<span class="title function_ invoke__">forSystem</span>(<span class="string">'You are a helpful assistant.'</span>),
|
||||
<span class="title class_">Message</span>::<span class="title function_ invoke__">ofUser</span>(<span class="string">'What is the Symfony framework?'</span>),
|
||||
);
|
||||
|
||||
<span class="variable">$result</span> = <span class="variable">$agent</span>-><span class="title function_ invoke__">call</span>(<span class="variable">$messages</span>);
|
||||
<span class="keyword">echo</span> <span class="variable">$result</span>-><span class="title function_ invoke__">getContent</span>();</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Token Usage #}
|
||||
<div class="feature-panel" data-feature-tabs-target="panel">
|
||||
<div class="row align-items-center g-4">
|
||||
<div class="col-lg-5">
|
||||
<h3 class="ff-title fw-bold h4 mb-3">Token Usage</h3>
|
||||
<p class="text-muted mb-4">
|
||||
Track exactly how many tokens each request consumes — prompt, completion, and total.
|
||||
Essential for monitoring costs, optimizing prompts, and staying within budget when running AI features in production.
|
||||
</p>
|
||||
<a href="https://github.com/symfony/ai/blob/main/examples/openai/token-metadata.php" class="btn btn-dark px-3 d-inline-flex align-items-center" target="_blank" rel="noopener noreferrer">
|
||||
{{ ux_icon('simple-icons:github', {width: 18, height: 18, class: 'me-2'}) }}
|
||||
<span>See example</span>
|
||||
{{ ux_icon('tabler:arrow-up-right', {width: 18, height: 18, class: 'ms-2'}) }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<pre class="terminal"><code><span class="variable">$result</span> = <span class="variable">$agent</span>-><span class="title function_ invoke__">call</span>(<span class="variable">$messages</span>);
|
||||
|
||||
<span class="variable">$usage</span> = <span class="variable">$result</span>-><span class="title function_ invoke__">getMetadata</span>()-><span class="title function_ invoke__">get</span>(<span class="string">'token_usage'</span>);
|
||||
|
||||
<span class="keyword">echo</span> <span class="variable">$usage</span>-><span class="title function_ invoke__">getPromptTokens</span>();
|
||||
<span class="keyword">echo</span> <span class="variable">$usage</span>-><span class="title function_ invoke__">getCompletionTokens</span>();
|
||||
<span class="keyword">echo</span> <span class="variable">$usage</span>-><span class="title function_ invoke__">getTotalTokens</span>();</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Tool Calling #}
|
||||
<div class="feature-panel" data-feature-tabs-target="panel">
|
||||
<div class="row align-items-center g-4">
|
||||
<div class="col-lg-5">
|
||||
<h3 class="ff-title fw-bold h4 mb-3">Tool Calling</h3>
|
||||
<p class="text-muted mb-4">
|
||||
Let AI models call your PHP functions to fetch data or trigger actions.
|
||||
Define tools as simple classes — the framework handles schema generation, argument passing, and result formatting automatically.
|
||||
</p>
|
||||
<a href="https://github.com/symfony/ai/blob/main/examples/openai/toolcall.php" class="btn btn-dark px-3 d-inline-flex align-items-center" target="_blank" rel="noopener noreferrer">
|
||||
{{ ux_icon('simple-icons:github', {width: 18, height: 18, class: 'me-2'}) }}
|
||||
<span>See example</span>
|
||||
{{ ux_icon('tabler:arrow-up-right', {width: 18, height: 18, class: 'ms-2'}) }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<pre class="terminal"><code><span class="variable">$toolbox</span> = <span class="keyword">new</span> <span class="title class_">Toolbox</span>([<span class="keyword">new</span> <span class="title class_">YoutubeTranscriber</span>(<span class="title function_ invoke__">http_client</span>())]);
|
||||
<span class="variable">$processor</span> = <span class="keyword">new</span> <span class="title class_">AgentProcessor</span>(<span class="variable">$toolbox</span>);
|
||||
<span class="variable">$agent</span> = <span class="keyword">new</span> <span class="title class_">Agent</span>(<span class="variable">$platform</span>, <span class="string">'gpt-5-mini'</span>, [<span class="variable">$processor</span>], [<span class="variable">$processor</span>]);
|
||||
|
||||
<span class="variable">$result</span> = <span class="variable">$agent</span>-><span class="title function_ invoke__">call</span>(<span class="variable">$messages</span>);</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Subagents #}
|
||||
<div class="feature-panel" data-feature-tabs-target="panel">
|
||||
<div class="row align-items-center g-4">
|
||||
<div class="col-lg-5">
|
||||
<h3 class="ff-title fw-bold h4 mb-3">Subagents</h3>
|
||||
<p class="text-muted mb-4">
|
||||
Compose complex workflows by using agents as tools for other agents.
|
||||
A coordinator agent can delegate specialized tasks — like math, research, or translation — to focused subagents, each with their own model and instructions.
|
||||
</p>
|
||||
<a href="https://github.com/symfony/ai/blob/main/examples/openai/agent-as-tool.php" class="btn btn-dark px-3 d-inline-flex align-items-center" target="_blank" rel="noopener noreferrer">
|
||||
{{ ux_icon('simple-icons:github', {width: 18, height: 18, class: 'me-2'}) }}
|
||||
<span>See example</span>
|
||||
{{ ux_icon('tabler:arrow-up-right', {width: 18, height: 18, class: 'ms-2'}) }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<pre class="terminal"><code><span class="variable">$mathAgent</span> = <span class="keyword">new</span> <span class="title class_">Agent</span>(<span class="variable">$platform</span>, <span class="string">'gpt-5.2'</span>, [<span class="variable">$mathPrompt</span>]);
|
||||
<span class="variable">$subagent</span> = <span class="keyword">new</span> <span class="title class_">Subagent</span>(<span class="variable">$mathAgent</span>);
|
||||
|
||||
<span class="variable">$toolbox</span> = <span class="keyword">new</span> <span class="title class_">Toolbox</span>([<span class="variable">$subagent</span>]);
|
||||
<span class="variable">$processor</span> = <span class="keyword">new</span> <span class="title class_">AgentProcessor</span>(<span class="variable">$toolbox</span>);
|
||||
<span class="variable">$agent</span> = <span class="keyword">new</span> <span class="title class_">Agent</span>(<span class="variable">$platform</span>, <span class="string">'gpt-5-mini'</span>, [<span class="variable">$processor</span>], [<span class="variable">$processor</span>]);
|
||||
|
||||
<span class="variable">$result</span> = <span class="variable">$agent</span>-><span class="title function_ invoke__">call</span>(<span class="variable">$messages</span>);</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# RAG #}
|
||||
<div class="feature-panel" data-feature-tabs-target="panel">
|
||||
<div class="row align-items-center g-4">
|
||||
<div class="col-lg-5">
|
||||
<h3 class="ff-title fw-bold h4 mb-3">RAG</h3>
|
||||
<p class="text-muted mb-4">
|
||||
Index documents into vector stores and retrieve relevant context automatically.
|
||||
Let your AI answer questions about your own data — manuals, blog posts, knowledge bases — by finding the most relevant passages before generating a response.
|
||||
</p>
|
||||
<a href="https://github.com/symfony/ai/blob/main/examples/rag/in-memory.php" class="btn btn-dark px-3 d-inline-flex align-items-center" target="_blank" rel="noopener noreferrer">
|
||||
{{ ux_icon('simple-icons:github', {width: 18, height: 18, class: 'me-2'}) }}
|
||||
<span>See example</span>
|
||||
{{ ux_icon('tabler:arrow-up-right', {width: 18, height: 18, class: 'ms-2'}) }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<pre class="terminal"><code><span class="variable">$vectorizer</span> = <span class="keyword">new</span> <span class="title class_">Vectorizer</span>(<span class="variable">$platform</span>, <span class="string">'text-embedding-3-small'</span>);
|
||||
<span class="variable">$processor</span> = <span class="keyword">new</span> <span class="title class_">DocumentProcessor</span>(<span class="variable">$vectorizer</span>, <span class="variable">$store</span>);
|
||||
<span class="variable">$indexer</span> = <span class="keyword">new</span> <span class="title class_">SourceIndexer</span>(<span class="variable">$loader</span>, <span class="variable">$processor</span>);
|
||||
<span class="variable">$indexer</span>-><span class="title function_ invoke__">index</span>([<span class="string">'/path/to/docs'</span>]);
|
||||
|
||||
<span class="variable">$retriever</span> = <span class="keyword">new</span> <span class="title class_">Retriever</span>(<span class="variable">$vectorizer</span>, <span class="variable">$store</span>);
|
||||
<span class="variable">$docs</span> = <span class="variable">$retriever</span>-><span class="title function_ invoke__">retrieve</span>(<span class="string">'How do I create a command?'</span>);</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
34
ai.symfony.com/templates/sections/_mate.html.twig
Normal file
34
ai.symfony.com/templates/sections/_mate.html.twig
Normal file
@@ -0,0 +1,34 @@
|
||||
<section id="mate" class="section-secondary py-5 py-lg-5">
|
||||
<div class="container">
|
||||
<div class="row align-items-center g-5">
|
||||
<div class="col-lg-7 order-lg-1 order-2" data-aos="fade-right">
|
||||
<pre class="terminal"><code><span class="comment"># Install Mate with extensions</span>
|
||||
$ composer require --dev symfony/ai-mate
|
||||
|
||||
<span class="comment"># Symfony extension: profiler, services</span>
|
||||
$ composer require --dev symfony/ai-symfony-mate-extension
|
||||
|
||||
<span class="comment"># Monolog extension: log search, filtering</span>
|
||||
$ composer require --dev symfony/ai-monolog-mate-extension
|
||||
|
||||
<span class="comment"># Initialize and start the MCP server</span>
|
||||
$ vendor/bin/mate init
|
||||
$ vendor/bin/mate serve</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-5 order-lg-2 order-1" data-aos="fade-left">
|
||||
<h2 class="ff-title fw-bold h3 mb-3 section-heading">Symfony Mate</h2>
|
||||
<p class="text-muted mb-4">
|
||||
An MCP server for AI-assisted development. Mate gives AI assistants like Claude Code, Copilot, or Codex
|
||||
direct access to your Symfony application — profiler data, logs, container services — so they
|
||||
can help you debug and understand your app in real time.
|
||||
</p>
|
||||
|
||||
<a href="https://symfony.com/doc/current/ai/components/mate.html" class="btn btn-dark px-3 d-flex align-items-center d-inline-flex">
|
||||
<span>Mate docs</span>
|
||||
{{ ux_icon('tabler:arrow-up-right', {width: 20, height: 20, class: 'ms-2'}) }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
39
ai.symfony.com/templates/sections/_mcp.html.twig
Normal file
39
ai.symfony.com/templates/sections/_mcp.html.twig
Normal file
@@ -0,0 +1,39 @@
|
||||
<section id="mcp" class="py-5 py-lg-5">
|
||||
<div class="container">
|
||||
<div class="row align-items-center g-5">
|
||||
<div class="col-lg-5" data-aos="fade-right">
|
||||
<h2 class="ff-title fw-bold h3 mb-3 section-heading">Model Context Protocol</h2>
|
||||
<p class="text-muted mb-4">
|
||||
The official MCP PHP SDK, built in collaboration with the PHP Foundation.
|
||||
Create MCP servers that expose tools and resources to any AI agent, or build
|
||||
clients that connect to existing MCP servers.
|
||||
</p>
|
||||
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<a href="https://symfony.com/doc/current/ai/bundles/mcp-bundle.html" class="btn btn-dark px-3 d-flex align-items-center">
|
||||
<span>MCP Bundle docs</span>
|
||||
{{ ux_icon('tabler:arrow-up-right', {width: 20, height: 20, class: 'ms-2'}) }}
|
||||
</a>
|
||||
<a href="https://php.sdk.modelcontextprotocol.io" class="btn btn-outline-secondary px-3 d-flex align-items-center">
|
||||
<span>Official SDK docs</span>
|
||||
{{ ux_icon('tabler:arrow-up-right', {width: 20, height: 20, class: 'ms-2'}) }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-7" data-aos="fade-left">
|
||||
<pre class="terminal"><code><span class="keyword">class</span> <span class="title class_">WeatherTool</span>
|
||||
{
|
||||
<span class="comment">/**
|
||||
* @param string $city Name of the city
|
||||
*/</span>
|
||||
<span class="title">#[McpTool(name: 'weather')]</span>
|
||||
<span class="keyword">public function</span> <span class="title function_ invoke__">getWeather</span>(<span class="variable">string</span> <span class="variable">$city</span>): <span class="variable">string</span>
|
||||
{
|
||||
<span class="keyword">return</span> <span class="string">'sunny, 24°C'</span>;
|
||||
}
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
114
ai.symfony.com/templates/sections/_third-party.html.twig
Normal file
114
ai.symfony.com/templates/sections/_third-party.html.twig
Normal file
@@ -0,0 +1,114 @@
|
||||
<section id="bridges" class="py-5 py-lg-5">
|
||||
<div class="container">
|
||||
<h2 class="ff-title fw-bold h3 mb-5 text-center section-heading" data-aos="fade-up">Third Party Integration Bridges</h2>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6 col-lg-3" data-aos="fade-up" data-aos-delay="0">
|
||||
<article class="sf-ai-card sf-ai-card-hover sf-ai-card-hover-dark sf-ai-card-glow sf-ai-card-shift p-3 p-lg-4">
|
||||
<h3 class="ff-title fw-bold h4 mb-2 text-center">Models</h3>
|
||||
<hr class="mb-4 opacity-10">
|
||||
<div class="sf-ai-card-body sf-ai-logo-grid">
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:openai') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:google-gemini') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:mistral') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:claude') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<hr class="mt-4 mb-3 opacity-10">
|
||||
<a href="https://symfony.com/doc/current/ai/components/platform.html#supported-models-platforms" class="small fw-semibold d-flex align-items-center justify-content-center gap-1" target="_blank" rel="noopener noreferrer">
|
||||
<span>See all models</span>
|
||||
{{ ux_icon('tabler:arrow-right', {width: 14, height: 14}) }}
|
||||
</a>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-3" data-aos="fade-up" data-aos-delay="100">
|
||||
<article class="sf-ai-card sf-ai-card-hover sf-ai-card-hover-dark sf-ai-card-glow sf-ai-card-shift p-3 p-lg-4">
|
||||
<h3 class="ff-title fw-bold h4 mb-2 text-center">Platforms</h3>
|
||||
<hr class="mb-4 opacity-10">
|
||||
<div class="sf-ai-card-body sf-ai-logo-grid">
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:hugging-face') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:azure') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:ollama') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:docker') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<hr class="mt-4 mb-3 opacity-10">
|
||||
<a href="https://symfony.com/doc/current/ai/components/platform.html#supported-models-platforms" class="small fw-semibold d-flex align-items-center justify-content-center gap-1" target="_blank" rel="noopener noreferrer">
|
||||
<span>See all platforms</span>
|
||||
{{ ux_icon('tabler:arrow-right', {width: 14, height: 14}) }}
|
||||
</a>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-3" data-aos="fade-up" data-aos-delay="200">
|
||||
<article class="sf-ai-card sf-ai-card-hover sf-ai-card-hover-dark sf-ai-card-glow sf-ai-card-shift p-3 p-lg-4">
|
||||
<h3 class="ff-title fw-bold h4 mb-2 text-center">Stores</h3>
|
||||
<hr class="mb-4 opacity-10">
|
||||
<div class="sf-ai-card-body sf-ai-logo-grid">
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:pinecone') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:postgresql') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:mariadb') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:chroma') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<hr class="mt-4 mb-3 opacity-10">
|
||||
<a href="https://symfony.com/doc/current/ai/components/store.html#supported-stores" class="small fw-semibold d-flex align-items-center justify-content-center gap-1" target="_blank" rel="noopener noreferrer">
|
||||
<span>See all stores</span>
|
||||
{{ ux_icon('tabler:arrow-right', {width: 14, height: 14}) }}
|
||||
</a>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-3" data-aos="fade-up" data-aos-delay="300">
|
||||
<article class="sf-ai-card sf-ai-card-hover sf-ai-card-hover-dark sf-ai-card-glow sf-ai-card-shift p-3 p-lg-4">
|
||||
<h3 class="ff-title fw-bold h4 mb-2 text-center">Built-in Tools</h3>
|
||||
<hr class="mb-4 opacity-10">
|
||||
<div class="sf-ai-card-body sf-ai-logo-grid">
|
||||
<span class="logo-pill">
|
||||
Similarity Search (RAG)
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:wikipedia') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:youtube') }}
|
||||
</span>
|
||||
<span class="logo-pill">
|
||||
{{ ux_icon('logos:brave') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<hr class="mt-4 mb-3 opacity-10">
|
||||
<a href="https://symfony.com/doc/current/ai/components/agent.html#code-examples-with-built-in-tools" class="small fw-semibold d-flex align-items-center justify-content-center gap-1" target="_blank" rel="noopener noreferrer">
|
||||
<span>See all tools</span>
|
||||
{{ ux_icon('tabler:arrow-right', {width: 14, height: 14}) }}
|
||||
</a>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
Reference in New Issue
Block a user