Skip to content

Commit aa889e2

Browse files
committed
Merge branch 'main' into DOC-5948
2 parents 99388a8 + 3e0c3dc commit aa889e2

File tree

323 files changed

+73473
-792
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

323 files changed

+73473
-792
lines changed

.github/workflows/redisvl_docs_sync.yaml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ jobs:
4040

4141
- name: 'Install deps'
4242
run: |
43-
pip3 install -U sphinx
44-
pip3 install sphinx_design sphinx_copybutton nbsphinx sphinx_favicon myst_nb sphinx_markdown_builder==0.6.8 jupyter
43+
pip3 install sphinx sphinx_design sphinx_copybutton nbsphinx==0.9.7 sphinx_favicon myst_nb sphinx_markdown_builder==0.6.8 jupyter
4544
4645
- name: 'Fetch redisvl repo'
4746
run: |
@@ -338,4 +337,4 @@ jobs:
338337
--head "$branch" \
339338
--base "main"
340339
fi
341-
fi
340+
fi

assets/css/index.css

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ section.prose {
9494
padding: 1rem;
9595
}
9696

97+
.prose pre.decision-tree-source {
98+
display: none;
99+
}
100+
97101
.prose pre > code {
98102
@apply bg-none font-monogeist;
99103
}
@@ -1074,6 +1078,21 @@ a[href*="#no-click"], img[src*="#no-click"] {
10741078
background-color: #ddd;
10751079
}
10761080

1081+
/* Copy button wrapper positioning */
1082+
.copy-button-wrapper {
1083+
position: absolute;
1084+
top: 24px;
1085+
right: 10px;
1086+
z-index: 1;
1087+
display: flex;
1088+
align-items: center;
1089+
gap: 6px;
1090+
}
1091+
1092+
.copy-button-wrapper.expand-content-wrapper {
1093+
right: 20px;
1094+
}
1095+
10771096
/* AI Agent Builder Styles */
10781097

10791098
.agent-builder-container {
@@ -1225,6 +1244,51 @@ a[href*="#no-click"], img[src*="#no-click"] {
12251244
}
12261245
}
12271246

1247+
/* Fix copy button overlap on smaller screens */
1248+
@media (max-width: 768px) {
1249+
/* Ensure code blocks have enough padding on the right to accommodate copy button */
1250+
.highlight {
1251+
padding-right: 60px !important;
1252+
}
1253+
1254+
/* Adjust copy button wrapper positioning for smaller screens */
1255+
.copy-button-wrapper {
1256+
right: 8px !important;
1257+
top: 8px !important;
1258+
}
1259+
1260+
.copy-button-wrapper.expand-content-wrapper {
1261+
right: 12px !important;
1262+
}
1263+
1264+
/* Make copy buttons slightly smaller on mobile */
1265+
.copy-button-wrapper .clipboard-button,
1266+
.copy-button-wrapper .download-yaml-btn {
1267+
width: 28px !important;
1268+
height: 28px !important;
1269+
padding: 2px !important;
1270+
}
1271+
}
1272+
1273+
@media (max-width: 480px) {
1274+
/* Even more padding for very small screens */
1275+
.highlight {
1276+
padding-right: 70px !important;
1277+
}
1278+
1279+
/* Stack buttons vertically on very small screens if both copy and download are present */
1280+
.copy-button-wrapper {
1281+
flex-direction: column !important;
1282+
gap: 4px !important;
1283+
right: 4px !important;
1284+
top: 4px !important;
1285+
}
1286+
1287+
.copy-button-wrapper.expand-content-wrapper {
1288+
right: 8px !important;
1289+
}
1290+
}
1291+
12281292
/* Form Groups */
12291293
.form-group {
12301294
@apply space-y-2;

build/components/example.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
'java-async': '//',
2929
'java-reactive': '//',
3030
'go': '//',
31+
'c': '//',
3132
'c#': '//',
3233
'c#-sync': '//',
3334
'c#-async': '//',

build/components/syntax.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ class ArgumentType(Enum):
2020
BLOCK = 'block'
2121
PURE_TOKEN = 'pure-token'
2222
COMMAND = 'command'
23+
FUNCTION = 'function'
24+
INDEX = 'index'
25+
KEYNUM = 'keynum'
26+
KEYWORD = 'keyword'
27+
RANGE = 'range'
28+
UNKNOWN = 'unknown'
2329

2430

2531
class Argument:
@@ -28,8 +34,8 @@ def __init__(self, data: dict = {}, level: int = 0, max_width: int = 640) -> Non
2834
self._stack = []
2935
self._level: int = level
3036
self._max_width: int = max_width
31-
self._name: str = data['name']
32-
self._type = ArgumentType(data['type'])
37+
self._name: str = data.get('name', data.get('token', 'unnamed'))
38+
self._type = ArgumentType(data.get('type', 'string'))
3339
self._optional: bool = data.get('optional', False)
3440
self._multiple: bool = data.get('multiple', False)
3541
self._multiple_token: bool = data.get('multiple_token', False)
@@ -49,6 +55,11 @@ def syntax(self, **kwargs) -> str:
4955
args += ' '.join([arg.syntax() for arg in self._arguments])
5056
elif self._type == ArgumentType.ONEOF:
5157
args += f' | '.join([arg.syntax() for arg in self._arguments])
58+
elif self._type == ArgumentType.FUNCTION:
59+
# Functions should display their token/name, not expand nested arguments
60+
args += self._display
61+
if show_types:
62+
args += f':{self._type.value}'
5263
elif self._type != ArgumentType.PURE_TOKEN:
5364
args += self._display
5465
if show_types:

build/local_examples.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
'.py': 'python',
2424
'.js': 'node.js',
2525
'.go': 'go',
26+
'.c': 'c',
27+
'.h': 'c',
2628
'.cs': 'c#',
2729
'.java': 'java',
2830
'.php': 'php',
@@ -34,6 +36,7 @@
3436
'python': 'Python',
3537
'node.js': 'Node.js',
3638
'go': 'Go',
39+
'c': 'C',
3740
'c#': 'C#-Sync',
3841
'java': 'Java-Sync', # Default to sync, could be overridden
3942
'php': 'PHP',
@@ -57,12 +60,15 @@ def get_client_name_from_language_and_path(language: str, path: str) -> str:
5760
"""Get client name from language with path-based overrides.
5861
5962
For Java (.java) files, override based on path substrings:
63+
- If 'lettuce-sync' in path -> Lettuce-Sync
6064
- If 'lettuce-async' in path -> Java-Async
6165
- If 'lettuce-reactive' in path -> Java-Reactive
6266
6367
Substring checks are case-sensitive and can appear anywhere in the path.
6468
"""
6569
if language == 'java':
70+
if 'lettuce-sync' in path:
71+
return 'Lettuce-Sync'
6672
if 'lettuce-async' in path:
6773
return 'Java-Async'
6874
if 'lettuce-reactive' in path:

build/render_hook_docs/AI_RENDER_HOOK_LESSONS.md

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,23 +409,208 @@ if ((metaValue.startsWith('"') && metaValue.endsWith('"'))) {
409409

410410
---
411411

412+
## 21. Server-Side Metadata Extraction for AI Agents
413+
414+
**Pattern**: Extract metadata from structured content (YAML, JSON) in the Hugo render hook and embed it as JSON in the HTML output for AI agents that don't execute JavaScript.
415+
416+
**Implementation**:
417+
```html
418+
{{- /* Extract top-level fields only (no indentation) */ -}}
419+
{{- $lines := split .Inner "\n" -}}
420+
{{- $id := "" -}}
421+
{{- range $lines -}}
422+
{{- /* Check if line starts without whitespace (32=space, 9=tab) */ -}}
423+
{{- if and (gt (len .) 0) (ne (index . 0) 32) (ne (index . 0) 9) -}}
424+
{{- $trimmed := strings.TrimSpace . -}}
425+
{{- if strings.HasPrefix $trimmed "id:" -}}
426+
{{- $afterPrefix := strings.Replace $trimmed "id:" "" 1 -}}
427+
{{- $id = strings.TrimSpace $afterPrefix -}}
428+
{{- end -}}
429+
{{- end -}}
430+
{{- end -}}
431+
432+
{{- /* Embed as JSON for AI agents */ -}}
433+
{{- $metadata := dict "type" "decision-tree" "id" $id -}}
434+
{{ $jsonMetadata := $metadata | jsonify (dict "indent" " ") }}
435+
{{ printf "<script type=\"application/json\" data-redis-metadata=\"decision-tree\">\n%s\n</script>" $jsonMetadata | safeHTML }}
436+
```
437+
438+
**Key Considerations**:
439+
- **Indentation detection**: When parsing nested structures, only extract top-level fields by checking if the line starts with whitespace
440+
- **String manipulation**: Use `strings.Replace` instead of `strings.TrimPrefix` for more reliable extraction
441+
- **Character codes**: Use ASCII codes (32 for space, 9 for tab) to detect indentation reliably
442+
- **Metadata format**: Use simple JSON (not JSON-LD) for clarity and ease of parsing
443+
- **Data attributes**: Use `data-*` attributes to mark metadata elements for AI agents
444+
445+
**Why This Matters**:
446+
- AI agents typically don't execute JavaScript, so metadata must be in static HTML
447+
- Server-side extraction ensures metadata is available even if JavaScript fails
448+
- Structured metadata helps AI agents understand the purpose and scope of components
449+
- Separating metadata from content improves maintainability
450+
451+
**Lesson**: Always provide metadata in static HTML for AI agents. Use server-side extraction to ensure accuracy and avoid relying on JavaScript parsing.
452+
453+
---
454+
455+
## 22. Handling Nested Structures in Hugo Templates
456+
457+
**Pattern**: When extracting data from nested YAML/JSON structures, distinguish between top-level and nested fields using indentation detection.
458+
459+
**Problem**: If you extract all occurrences of a field (e.g., `id:`), you'll get nested occurrences too, leading to incorrect values.
460+
461+
**Solution**: Check indentation before processing:
462+
```html
463+
{{- if and (gt (len .) 0) (ne (index . 0) 32) (ne (index . 0) 9) -}}
464+
{{- /* Process only top-level lines */ -}}
465+
{{- end -}}
466+
```
467+
468+
**Why This Works**:
469+
- YAML indentation is significant and indicates nesting level
470+
- Top-level fields have no leading whitespace
471+
- Nested fields have leading spaces or tabs
472+
- Character code 32 = space, 9 = tab
473+
474+
**Lesson**: When parsing nested structures in Hugo templates, use indentation detection to distinguish between levels. This prevents extracting nested values when you only want top-level ones.
475+
476+
---
477+
478+
## 23. Progressive Enhancement with Metadata
479+
480+
**Pattern**: Combine progressive enhancement with metadata embedding to serve both humans and AI agents from a single source.
481+
482+
**Architecture**:
483+
1. **Server-side (Hugo)**:
484+
- Extract metadata from source content
485+
- Embed metadata as JSON in HTML
486+
- Preserve raw source in `<pre>` element
487+
488+
2. **Client-side (JavaScript)**:
489+
- Parse raw source for rendering
490+
- Use metadata for context/identification
491+
- Enhance with interactivity
492+
493+
3. **AI agents**:
494+
- Read static JSON metadata
495+
- Parse raw source from `<pre>` element
496+
- No JavaScript execution needed
497+
498+
**Benefits**:
499+
- Single source of truth (the YAML/JSON in the Markdown)
500+
- Metadata available to all consumers (humans, AI agents, JavaScript)
501+
- Graceful degradation if JavaScript fails
502+
- AI-friendly without extra work
503+
504+
**Lesson**: Design render hooks to serve multiple audiences simultaneously. Metadata should be available in static HTML, not just in JavaScript.
505+
506+
---
507+
508+
## 24. Text Wrapping and Box Sizing in SVG Diagrams
509+
510+
**Pattern**: When rendering text in SVG boxes, calculate dimensions based on character width and implement text wrapping to fit within maximum width.
511+
512+
**Implementation**:
513+
```javascript
514+
const charWidth = 8; // Space Mono at 14px
515+
const maxBoxWidth = 420;
516+
const maxCharsPerLine = Math.floor(maxBoxWidth / charWidth);
517+
518+
function wrapText(text, maxChars) {
519+
const words = text.split(' ');
520+
const lines = [];
521+
let currentLine = '';
522+
523+
for (const word of words) {
524+
if ((currentLine + ' ' + word).length > maxChars) {
525+
if (currentLine) lines.push(currentLine);
526+
currentLine = word;
527+
} else {
528+
currentLine = currentLine ? currentLine + ' ' + word : word;
529+
}
530+
}
531+
if (currentLine) lines.push(currentLine);
532+
return lines;
533+
}
534+
```
535+
536+
**Considerations**:
537+
- **Font metrics**: Different fonts have different character widths
538+
- **Padding**: Account for box padding when calculating available width
539+
- **Line height**: Multiply number of lines by line height for total box height
540+
- **Dynamic sizing**: Calculate SVG dimensions based on content, not fixed values
541+
542+
**Common Pitfall**: Hardcoding SVG width can cause content to be cut off. Instead:
543+
```javascript
544+
const svgWidth = leftMargin + (maxDepth + 1) * indentWidth + maxBoxWidth + 40;
545+
```
546+
547+
**Lesson**: Calculate SVG dimensions dynamically based on content. Account for all visual elements (padding, margins, decorations) when sizing boxes and containers.
548+
549+
---
550+
551+
## 25. Scope and Context Metadata for Component Discovery
552+
553+
**Pattern**: Add `scope` or `category` metadata to components to help AI agents understand their purpose and applicability.
554+
555+
**Implementation**:
556+
```yaml
557+
id: documents-tree
558+
scope: documents
559+
rootQuestion: root
560+
questions:
561+
# ...
562+
```
563+
564+
**Benefits**:
565+
- **Discoverability**: AI agents can filter components by scope
566+
- **Context awareness**: Agents know which problem domain each component addresses
567+
- **Prevents misapplication**: Agents won't use a "collections" tree to recommend document storage
568+
- **Relationship mapping**: Enables linking related components
569+
570+
**Use Cases**:
571+
- Filtering decision trees by data type category
572+
- Finding all components related to a specific feature
573+
- Organizing components hierarchically
574+
- Providing context in search results
575+
576+
**Lesson**: Add semantic metadata (scope, category, type) to components. This helps AI agents understand purpose and applicability, enabling better recommendations and filtering.
577+
578+
---
579+
412580
## Quick Checklist for Future Render Hooks
413581

582+
### Core Patterns
414583
- [ ] Preserve source content in a `<pre>` or similar element
415584
- [ ] Use page store pattern to avoid duplicate resource loading
416585
- [ ] Place static JavaScript in `static/js/`, not `assets/js/`
417586
- [ ] Avoid `innerHTML` with dynamic content; use safe DOM methods
418587
- [ ] Use `data-*` attributes to pass server data to JavaScript
588+
589+
### Testing & Accessibility
419590
- [ ] Test with multiple instances on the same page
420591
- [ ] Consider state persistence if needed
421592
- [ ] Use semantic HTML and proper accessibility attributes
422593
- [ ] Document the Markdown format clearly
423594
- [ ] Provide sensible defaults for optional parameters
595+
596+
### Hugo-Specific
424597
- [ ] Remember Hugo converts attribute names to lowercase
598+
- [ ] Use indentation detection when parsing nested structures
599+
- [ ] Extract top-level metadata in render hook (not JavaScript)
600+
- [ ] Use `strings.Replace` for reliable string manipulation in templates
601+
602+
### Advanced Features
425603
- [ ] Use SVG for complex visual structures
426604
- [ ] Track processed lines when parsing nested structures
427605
- [ ] Account for all visual elements in dimension calculations
428606
- [ ] Use type attributes for conditional features
429607
- [ ] Handle string unescaping during parsing, not rendering
430608
- [ ] Create comprehensive format documentation
431609

610+
### AI Agent Compatibility
611+
- [ ] Embed metadata as JSON in static HTML (not just JavaScript)
612+
- [ ] Add `scope` or `category` metadata for component discovery
613+
- [ ] Use `data-*` attributes to mark metadata elements
614+
- [ ] Ensure metadata is available without JavaScript execution
615+
- [ ] Preserve raw source content for AI parsing
616+

0 commit comments

Comments
 (0)