1mod agentic_tool_capabilities;
25mod brainstorm;
26mod client_integrations;
27mod coreference;
28mod embedded;
29mod facts;
30mod grounding_overrides;
31mod handler_precedence;
32mod market_price_references;
33mod meanings;
34mod model_aliases;
35mod operation_vocabulary;
36pub(crate) mod parser;
37mod personas;
38mod projects;
39mod roles;
40mod shell_intents;
41mod summary_topics;
42mod terminal_commands;
43
44use std::collections::BTreeMap;
45
46use parser::{
47 escape_value, find_closing_quote, parse_codepoint, parse_lino, split_pipe_list, unescape_value,
48 LinoNode,
49};
50
51pub use agentic_tool_capabilities::{agentic_tool_capabilities, AgenticToolCapability};
52pub use brainstorm::{brainstorm_seeds, BrainstormCategory, BrainstormSeeds};
53pub use client_integrations::{
54 client_integrations, ClientIntegration, ClientIntegrationGlobalConfig,
55 ClientIntegrationInvocation, ConfigFormat, ModeArgPosition, ModelArgPosition, TemplateEnv,
56};
57pub use coreference::{coreference_seeds, Antecedent, CoreferenceSeeds, Pronoun};
58pub use embedded::{
59 seed_files, AGENTIC_TOOL_CAPABILITIES_LINO, AGENT_INFO_LINO, BRAINSTORM_SEEDS_LINO,
60 CLIENT_INTEGRATIONS_LINO, CODING_IDIOMS_LINO, CONCEPTS_LINO, CONCEPT_CONTEXTS_LINO,
61 COREFERENCE_LINO, DEMO_DIALOGS_LINO, ENVIRONMENTS_LINO, FACTS_LINO, GREETINGS_LINO,
62 HANDLER_PRECEDENCE_LINO, HELLO_WORLD_PROGRAMS_LINO, IDENTITY_LINO, INTENT_ROUTING_LINO,
63 LANGUAGE_DETECTION_LINO, LEARNING_SOURCES_LINO, MARKET_PRICE_REFERENCES_LINO,
64 MEANINGS_CALENDAR_LINO, MEANINGS_FACTS_LINO, MEANINGS_LINKS_ROOT_LINO, MEANINGS_LINO,
65 MEANINGS_SEMANTIC_META_LINO, MEANINGS_SOFTWARE_PROJECT_LINO, MEANINGS_UNITS_LINO,
66 MEANING_FILES, MODEL_ALIASES_LINO, MULTILINGUAL_RESPONSES_LINO, NUMERIC_LIST_OPERATIONS_LINO,
67 OPERATION_VOCABULARY_LINO, PERSONAS_LINO, PROGRAM_CST_GRAMMARS_LINO, PROGRAM_PLAN_RULES_LINO,
68 PROJECTS_LINO, PROMPT_PATTERNS_LINO, SELF_IMPROVEMENT_LOOP_LINO, SHELL_INTENTS_LINO,
69 SUMMARY_TOPICS_LINO, TERMINAL_COMMANDS_LINO, TOOLS_LINO,
70};
71pub use facts::{facts, FactRecord, LocalizedFact};
72pub use grounding_overrides::{
73 cache_contains, override_facts, override_reason, parse_record, resolve, OverrideFact,
74};
75pub use handler_precedence::{handler_precedence, handler_precedence_from};
76pub use market_price_references::{market_price_assets, MarketPriceAsset, MarketPricePeriod};
77pub use meanings::{
78 lexicon, parse_lexicon_text, ArithmeticOperator, Lexeme, Lexicon, Meaning, SemanticFacet, Slot,
79 WordForm,
80};
81pub use model_aliases::{
82 canonical_model_id, model_aliases, resolve_model_id, try_resolve_model_id, ModelAliasRegistry,
83};
84pub use operation_vocabulary::{
85 operation_vocabulary, OperationLanguageForms, OperationTrigger, OperationVocabulary,
86};
87pub use personas::{persona_seeds, Persona, PersonaSeeds, PersonaTopic};
88pub use projects::{
89 projects_registry, LocalizedProject, ProjectRecord, ProjectStatement, ProjectsRegistry,
90};
91pub use roles::{
92 ROLE_AGENT_ACTION_REPORT_SUBJECT, ROLE_AGENT_ACTION_REPORT_VERB, ROLE_ANSWER_RATIONALE_LEAD,
93 ROLE_ARCHITECTURE_CONCEPT, ROLE_ARITHMETIC_OPERATOR_WORD, ROLE_ASSISTANT_MECHANISM_INQUIRY,
94 ROLE_ASSISTANT_SELF_REFERENCE, ROLE_BEHAVIOR_RULE_EDIT_DIRECTIVE,
95 ROLE_BINARY_RELATION_PROPERTY, ROLE_CALCULATION_BASIS_REFERENCE, ROLE_CALCULATION_DOMAIN_TERM,
96 ROLE_CALCULATION_REQUEST_CUE, ROLE_CALCULATION_RESULT_QUERY_CUE, ROLE_CALCULATOR_TOOL_NAME,
97 ROLE_CALENDAR_DAY_REFERENCE, ROLE_CALENDAR_DIRECTION_NEXT, ROLE_CALENDAR_DIRECTION_PREVIOUS,
98 ROLE_CALENDAR_EVENT, ROLE_CALENDAR_QUESTION, ROLE_CALENDAR_RELATIVE_DATE,
99 ROLE_CALENDAR_SCHEDULE_ACTION, ROLE_CALENDAR_TIME, ROLE_CALENDAR_TIMEZONE_ALIAS,
100 ROLE_CALENDAR_TODAY, ROLE_CALENDAR_WEEKDAY, ROLE_CAPABILITY_QUERY, ROLE_CAPABILITY_QUERY_MORE,
101 ROLE_CARDINAL_NUMBER_WORD, ROLE_CAUSAL_INTERROGATIVE, ROLE_CIRCULAR_JOKE_PHRASE,
102 ROLE_CLARIFICATION_REQUEST, ROLE_CLAUSE_CONTINUATION_MARKER, ROLE_CODE_METHOD_NOUN,
103 ROLE_COMMON_TYPO, ROLE_COMPARISON_DIFFERENCE_CUE, ROLE_COMPARISON_TABLE_NOUN,
104 ROLE_COMPARISON_TABLE_TRIGGER, ROLE_COMPOSITIONAL_GENITIVE_HEAD, ROLE_COMPOSITIONAL_LEMMA,
105 ROLE_COMPOSITIONAL_PHRASE, ROLE_COMPOUNDING_ACTION_CUE, ROLE_COMPOUNDING_FREQUENCY_CUE,
106 ROLE_COMPREHENSION_FAILURE_MARKER, ROLE_CONVERSATION_RECALL_OTHER_QUERY,
107 ROLE_CONVERSATION_RECALL_PREVIOUS_MESSAGE, ROLE_CONVERSATION_RECALL_PREVIOUS_USER_MESSAGE,
108 ROLE_CONVERSATION_RECALL_QUERY, ROLE_CONVERSATION_REFERENCE,
109 ROLE_CONVERSATION_SUMMARY_COURTESY, ROLE_CONVERSATION_SUMMARY_DIRECTIVE,
110 ROLE_CONVERSATION_SUMMARY_PHRASE, ROLE_CONVERSATION_TOPIC_OPENER, ROLE_CONVERSION_ACTION_CUE,
111 ROLE_CURRENCY_EUR_REFERENCE, ROLE_CURRENCY_RUB_REFERENCE, ROLE_CURRENCY_USD_REFERENCE,
112 ROLE_DEFINITION_ARTIFACT_REQUEST, ROLE_DEFINITION_COMMAND, ROLE_DEFINITION_MERGE_ACTION,
113 ROLE_DEFINITION_MERGE_MARKER, ROLE_DEFINITION_MERGE_TAIL_BOUNDARY, ROLE_DETAIL_MODIFIER,
114 ROLE_DOCUMENT_ORIGINALITY_CHECK_ACTION, ROLE_DOCUMENT_ORIGINALITY_DOCUMENT,
115 ROLE_DOCUMENT_ORIGINALITY_SUBJECT, ROLE_ENUMERATION_CONSTRAINT,
116 ROLE_ENUMERATION_REQUEST_OPENER, ROLE_EXCHANGE_RATE_REFERENCE, ROLE_EXPLANATION_REQUEST_LEAD,
117 ROLE_FACT_RELATION, ROLE_FEATURE_ACTION_ARITHMETIC, ROLE_FEATURE_ACTION_PLANNING,
118 ROLE_FEATURE_CAPABILITY_ALIAS, ROLE_FEATURE_CAPABILITY_QUESTION, ROLE_FILE_EDIT_ACTION_CUE,
119 ROLE_FILE_EDIT_NEW_LEAD_CUE, ROLE_FILE_EDIT_TARGET_CUE, ROLE_FILE_READ_ACTION_CUE,
120 ROLE_FILE_WRITE_ACTION_CUE, ROLE_FILE_WRITE_CONTENT_LEAD, ROLE_FILE_WRITE_DESTINATION_CUE,
121 ROLE_FILE_WRITE_TARGET_CUE, ROLE_FINAL_AMOUNT_REFERENCE, ROLE_FOLLOWUP_INSTRUCTION_VERB,
122 ROLE_GAME_TRACKER_DOMAIN, ROLE_GAME_TRACKER_MECHANIC, ROLE_GITHUB_REPOSITORY_PLATFORM,
123 ROLE_GITHUB_REPOSITORY_TRAFFIC_QUESTION, ROLE_GITHUB_REPOSITORY_TRAFFIC_SIGNAL,
124 ROLE_HELLO_WORLD_REFERENCE, ROLE_HTTP_FETCH, ROLE_IMPLEMENTATION_LANGUAGE_NOUN,
125 ROLE_IMPLEMENTATION_LANGUAGE_PREPOSITION, ROLE_INTEREST_CUE, ROLE_INTERROGATIVE_OPENER,
126 ROLE_INVESTMENT_CUE, ROLE_KNOWLEDGE_INVENTORY_INTERROGATIVE, ROLE_KNOWLEDGE_INVENTORY_NOUN,
127 ROLE_KNOWLEDGE_INVENTORY_PHRASE, ROLE_KNOWLEDGE_POSSESSION, ROLE_LINKS_NOTATION_FORMAT,
128 ROLE_LIVE_RATE_FRESHNESS_CUE, ROLE_LOCAL_SHELL_REQUEST_CUE, ROLE_MATH_FUNCTION_NAME,
129 ROLE_MEASUREMENT_UNIT, ROLE_MECHANISM_INQUIRY, ROLE_MECHANISM_PREDICATE,
130 ROLE_MEMORY_APPEND_DIRECTIVE, ROLE_MEMORY_SCOPE, ROLE_MEMORY_SUBSTITUTION_CONNECTOR,
131 ROLE_MEMORY_SUBSTITUTION_DIRECTIVE, ROLE_NETWORK_CAPABILITY_CUE, ROLE_NONDETERMINISTIC_MARKER,
132 ROLE_NON_REFERENTIAL_SUBJECT, ROLE_OPERATING_PRINCIPLE, ROLE_OUTPUT_DISPLAY_REQUEST,
133 ROLE_PHYSICAL_ACTION_TRIGGER, ROLE_PHYSICAL_DIMENSION, ROLE_PLAYWRIGHT_SCRIPT_CUE,
134 ROLE_PLAYWRIGHT_TOOL_NAME, ROLE_POLITENESS_CUE, ROLE_PRIOR_ANSWER_REFERENCE,
135 ROLE_PROCEDURAL_ACTION_VERB, ROLE_PROCEDURAL_ELABORATION, ROLE_PROCEDURAL_REQUEST,
136 ROLE_PROCEDURAL_REQUEST_ELIDED_LEAD, ROLE_PROCEDURAL_TASK_MODIFIER, ROLE_PROGRAM_ARTIFACT,
137 ROLE_PROGRAM_GENUS, ROLE_PROGRAM_KIND, ROLE_PROGRAM_LANGUAGE_ALIAS, ROLE_PROGRAM_MODIFICATION,
138 ROLE_PROGRAM_REQUEST, ROLE_PROGRAM_SYNTHESIS_ACTION, ROLE_PROGRAM_SYNTHESIS_DOMAIN,
139 ROLE_PROGRAM_SYNTHESIS_SIGNAL, ROLE_PROGRAM_SYNTHESIS_SUBJECT, ROLE_PROGRAM_SYNTHESIS_TASK,
140 ROLE_PROGRAM_TASK_ALIAS, ROLE_PROOF_CLAIM_SCAFFOLD, ROLE_PROOF_CONCEPT_DETERMINISM,
141 ROLE_PROOF_CONCEPT_GODEL, ROLE_PROOF_DIRECTIVE, ROLE_PROOF_MARKER, ROLE_PROOF_REQUEST_LEAD,
142 ROLE_QUANTITY_CONVERSION_CUE, ROLE_REACHABILITY_OPERAND_FRAMING, ROLE_REACHABILITY_SEARCH_CUE,
143 ROLE_REACHABILITY_TARGET_MARKER, ROLE_REPOSITORY_REFERENCE, ROLE_RESEARCH_CRITERION,
144 ROLE_RESEARCH_EVALUATION_DOMAIN, ROLE_RESEARCH_EVIDENCE_DOMAIN, ROLE_RESEARCH_PROMPT_SIGNAL,
145 ROLE_RESEARCH_QUESTION_OPENER, ROLE_RESEARCH_SUPERLATIVE_MODIFIER,
146 ROLE_RESPONSE_LANGUAGE_MARKER, ROLE_RULE_BRIEF_REQUEST, ROLE_RULE_COUNT_REQUEST,
147 ROLE_RULE_COUNT_SCOPE, ROLE_RULE_LISTING_PHRASE, ROLE_RULE_LISTING_REQUEST,
148 ROLE_RULE_LISTING_SCOPE, ROLE_RULE_LISTING_SUBJECT, ROLE_SCRIPT_AUTHORING_VERB,
149 ROLE_SCRIPT_OR_CODE_ARTIFACT, ROLE_SELF_FACT_QUERY, ROLE_SELF_INTRODUCTION_REQUEST,
150 ROLE_SHELL_CAPABILITY_CUE, ROLE_SKILL_TEACHING_RESPONSE_VERB, ROLE_SKILL_TEACHING_TRIGGER_LEAD,
151 ROLE_SKILL_WHEN_THEN_PAIR, ROLE_SOFTWARE_APPROVAL_TRIGGER, ROLE_SOFTWARE_ARTIFACT_KIND,
152 ROLE_SOFTWARE_AUTHORING_ACTION, ROLE_SOFTWARE_BASH_COMMAND, ROLE_SOFTWARE_DELIVERY_MODE,
153 ROLE_SOFTWARE_FEATURE, ROLE_SOFTWARE_FOLLOWUP_DEMONSTRATION, ROLE_SOFTWARE_FOLLOWUP_EXECUTION,
154 ROLE_SOFTWARE_FOLLOWUP_VERIFICATION, ROLE_SOFTWARE_IMPLEMENTATION_LANGUAGE,
155 ROLE_SOFTWARE_REQUIREMENT_CATEGORY, ROLE_SOFTWARE_STEP_GRANULARITY,
156 ROLE_SUMMARY_CLASSIFICATION_CUE, ROLE_TERM_INFORMATION_REQUEST_OPENER, ROLE_TIME_DURATION_CUE,
157 ROLE_TOOL_ARGUMENT_MARKER, ROLE_TOOL_INVOCATION_CUE, ROLE_TOOL_RESULT_DETAIL_REQUEST,
158 ROLE_TOOL_RESULT_FIRST_REFERENCE, ROLE_TOOL_RESULT_LINE_REQUEST,
159 ROLE_TOOL_RESULT_SECOND_REFERENCE, ROLE_TOOL_RESULT_URL_REQUEST, ROLE_TOPIC_SCAN_STOP_WORD,
160 ROLE_TRANSLATION_ACTION, ROLE_TRANSLATION_INTO_MARKER, ROLE_TRANSLATION_OBJECT_MARKER,
161 ROLE_TRANSLATION_PROPERTY, ROLE_TRANSLATION_SOURCE_MARKER, ROLE_TRANSLATION_TARGET_DIRECTION,
162 ROLE_TRANSLATION_TARGET_MARKER, ROLE_TRANSLATION_UNQUOTED_FRAME, ROLE_URL_NAVIGATE,
163 ROLE_VULGAR_CONTENT_MARKER, ROLE_WEB_MEDIUM, ROLE_WEB_SEARCH_ACTION,
164 ROLE_WEB_SEARCH_EXPLICIT_PREFIX, ROLE_WEB_SEARCH_HISTORY_SIGNAL,
165 ROLE_WEB_SEARCH_IMPERATIVE_LEAD, ROLE_WEB_SEARCH_NEWS_RECENCY, ROLE_WEB_SEARCH_NEWS_SUBJECT,
166 ROLE_WEB_SEARCH_PUBLIC_EVENT_SUBJECT, ROLE_WEB_SEARCH_QUERY_LEADING_NOISE,
167 ROLE_WEB_SEARCH_QUERY_TRAILING_NOISE, ROLE_WEB_SEARCH_RECORDS_SUBJECT, ROLE_WEB_SEARCH_SIGNAL,
168 ROLE_WEB_SEARCH_SOURCE_ONLY, ROLE_WEB_SEARCH_STRONG_ACTION, ROLE_WEB_SEARCH_TOOL_NAME,
169 ROLE_WEB_SEARCH_TOPIC_MARKER, ROLE_WHO_QUESTION_LEAD, ROLE_WHO_QUESTION_TAIL,
170 ROLE_WIKIDATA_ENTITY_ANCHOR, ROLE_YEAR_UNIT_CUE,
171};
172pub use shell_intents::{
173 shell_intent_vocabulary, ShellIntent, ShellIntentArgument, ShellIntentVocabulary,
174};
175pub use summary_topics::{summary_topic_seeds, SummaryTopic, SummaryTopicSeeds};
176pub use terminal_commands::{terminal_command_vocabulary, TerminalCommandVocabulary};
177
178#[must_use]
185pub fn merged_bundle() -> String {
186 bundle_from_files(&seed_files())
187}
188
189#[must_use]
195pub fn bundle_from_files(files: &[(&str, &str)]) -> String {
196 let mut out = String::new();
197 out.push_str("formal_ai_seed_bundle\n");
198 for (name, contents) in files {
199 out.push_str(" file \"");
200 out.push_str(&escape_value(name));
201 out.push_str("\"\n");
202 for line in contents.lines() {
203 if line.is_empty() {
204 continue;
205 }
206 out.push_str(" ");
207 out.push_str(line);
208 out.push('\n');
209 }
210 }
211 out
212}
213
214#[must_use]
233pub fn parse_bundle(text: &str) -> Vec<(String, String)> {
234 let mut sections: Vec<(String, String)> = Vec::new();
235 let mut current_name: Option<String> = None;
236 let mut current_body = String::new();
237 let mut file_indent: usize = 2;
238 let mut body_indent: usize = 4;
239 let mut inside_seed_files = false;
240 for line in text.lines() {
241 if line.is_empty() {
242 if current_name.is_some() {
243 current_body.push('\n');
244 }
245 continue;
246 }
247 let indent = line.chars().take_while(|c| *c == ' ').count();
248 let trimmed = &line[indent..];
249 if indent == 0 {
252 if let Some(name) = current_name.take() {
253 sections.push((name, std::mem::take(&mut current_body)));
254 }
255 inside_seed_files = false;
256 file_indent = 2;
257 body_indent = 4;
258 continue;
259 }
260 if indent == 2 && trimmed == "seed_files" {
262 if let Some(name) = current_name.take() {
263 sections.push((name, std::mem::take(&mut current_body)));
264 }
265 inside_seed_files = true;
266 file_indent = 4;
267 body_indent = 6;
268 continue;
269 }
270 if inside_seed_files && indent == 2 {
273 if let Some(name) = current_name.take() {
274 sections.push((name, std::mem::take(&mut current_body)));
275 }
276 inside_seed_files = false;
277 continue;
278 }
279 if indent == file_indent && trimmed.starts_with("file ") {
281 if let Some(name) = current_name.take() {
282 sections.push((name, std::mem::take(&mut current_body)));
283 }
284 if let Some(rest) = trimmed.strip_prefix("file ") {
285 let rest = rest.trim();
286 if let Some(stripped) = rest.strip_prefix('"') {
287 if let Some(close) = find_closing_quote(stripped) {
288 current_name = Some(unescape_value(&stripped[..close]));
289 }
290 }
291 }
292 continue;
293 }
294 if current_name.is_some() {
296 let prefix: String = " ".repeat(body_indent);
297 let stripped = line
298 .strip_prefix(prefix.as_str())
299 .unwrap_or_else(|| line.trim_start());
300 current_body.push_str(stripped);
301 current_body.push('\n');
302 }
303 }
304 if let Some(name) = current_name.take() {
305 sections.push((name, current_body));
306 }
307 sections
308}
309
310#[derive(Debug, Clone)]
312pub struct ResponseRecord {
313 pub id: String,
314 pub intent: String,
315 pub language: String,
316 pub text: String,
317}
318
319#[must_use]
321pub fn multilingual_responses() -> Vec<ResponseRecord> {
322 let tree = parse_lino(MULTILINGUAL_RESPONSES_LINO);
323 let mut out = Vec::new();
324 if let Some(root) = tree.children.first() {
325 for entry in root.children.iter().filter(|c| c.name == "response") {
326 let intent = entry.find_child_value("intent").to_string();
327 let language = entry.find_child_value("language").to_string();
328 let text = entry.find_child_value("text").to_string();
329 if intent.is_empty() || language.is_empty() {
330 continue;
331 }
332 out.push(ResponseRecord {
333 id: entry.id.clone(),
334 intent,
335 language,
336 text,
337 });
338 }
339 }
340 out
341}
342
343#[must_use]
346pub fn response_for(intent: &str, language: &str) -> Option<String> {
347 for record in multilingual_responses() {
348 if record.intent == intent && record.language == language {
349 return Some(record.text);
350 }
351 }
352 None
353}
354
355#[must_use]
357pub fn agent_info() -> BTreeMap<String, String> {
358 let tree = parse_lino(AGENT_INFO_LINO);
359 let mut out = BTreeMap::new();
360 if let Some(root) = tree.children.first() {
361 for entry in root.children.iter().filter(|c| c.name == "field") {
362 let key = entry.id.clone();
363 let value = entry.find_child_value("value").to_string();
364 if !key.is_empty() {
365 out.insert(key, value);
366 }
367 }
368 }
369 out
370}
371
372#[must_use]
379pub fn supported_languages() -> Vec<String> {
380 agent_info()
381 .get("supported_languages")
382 .map(|value| split_pipe_list(value))
383 .unwrap_or_default()
384}
385
386#[derive(Debug, Clone)]
388pub struct LanguageRule {
389 pub id: String,
390 pub language: String,
391 pub label: String,
392 pub start: u32,
393 pub end: u32,
394}
395
396#[must_use]
397pub fn language_rules() -> Vec<LanguageRule> {
398 let tree = parse_lino(LANGUAGE_DETECTION_LINO);
399 let mut out = Vec::new();
400 if let Some(root) = tree.children.first() {
401 for entry in root.children.iter().filter(|c| c.name == "rule") {
402 let language = entry.find_child_value("language").to_string();
403 if language.is_empty() {
404 continue;
405 }
406 out.push(LanguageRule {
407 id: entry.id.clone(),
408 language,
409 label: entry.find_child_value("label").to_string(),
410 start: parse_codepoint(entry.find_child_value("start")),
411 end: parse_codepoint(entry.find_child_value("end")),
412 });
413 }
414 }
415 out
416}
417
418#[derive(Debug, Clone)]
420pub struct PromptPattern {
421 pub id: String,
422 pub intent: String,
423 pub language: String,
424 pub kind: String,
425 pub text: String,
426}
427
428#[derive(Debug, Clone, Default)]
434pub struct LocalizedConcept {
435 pub language: String,
436 pub term: String,
437 pub aliases: Vec<String>,
438 pub summary: String,
439 pub source: String,
440 pub source_kind: String,
441}
442
443#[derive(Debug, Clone)]
464pub struct ConceptRecord {
465 pub slug: String,
466 pub term: String,
467 pub category: String,
468 pub aliases: Vec<String>,
469 pub contexts: Vec<String>,
470 pub context_links: Vec<String>,
471 pub wikidata: String,
472 pub summary: String,
473 pub source: String,
474 pub source_kind: String,
475 pub localized: Vec<LocalizedConcept>,
476}
477
478impl ConceptRecord {
479 #[must_use]
482 pub fn localized_for(&self, language: &str) -> Option<&LocalizedConcept> {
483 self.localized
484 .iter()
485 .find(|loc| loc.language == language)
486 .or_else(|| self.localized.iter().find(|loc| loc.language == "en"))
487 }
488}
489
490#[must_use]
491pub fn concepts() -> Vec<ConceptRecord> {
492 let tree = parse_lino(CONCEPTS_LINO);
493 let mut out = Vec::new();
494 let entries: &[LinoNode] = if tree.name.is_empty() {
495 tree.children.as_slice()
496 } else {
497 std::slice::from_ref(&tree)
498 };
499 for entry in entries {
500 if !entry.name.starts_with("concept_") {
501 continue;
502 }
503 let aliases = split_pipe_list(entry.find_child_value("aliases"));
504 let contexts = split_pipe_list(entry.find_child_value("contexts"));
505 let context_links = split_pipe_list(entry.find_child_value("context_links"));
506 let summary = entry.find_child_value("summary").to_string();
507 let term = entry.find_child_value("term").to_string();
508 if term.is_empty() || summary.is_empty() {
509 continue;
510 }
511 let mut localized = Vec::new();
512 for child in entry.children.iter().filter(|c| c.name == "localized") {
513 let lang = child.id.clone();
514 if lang.is_empty() {
515 continue;
516 }
517 localized.push(LocalizedConcept {
518 language: lang,
519 term: child.find_child_value("term").to_string(),
520 aliases: split_pipe_list(child.find_child_value("aliases")),
521 summary: child.find_child_value("summary").to_string(),
522 source: child.find_child_value("source").to_string(),
523 source_kind: child.find_child_value("source_kind").to_string(),
524 });
525 }
526 out.push(ConceptRecord {
527 slug: entry.name.clone(),
528 term,
529 category: entry.find_child_value("category").to_string(),
530 aliases,
531 contexts,
532 context_links,
533 wikidata: entry.find_child_value("wikidata").to_string(),
534 summary,
535 source: entry.find_child_value("source").to_string(),
536 source_kind: entry.find_child_value("source_kind").to_string(),
537 localized,
538 });
539 }
540 out
541}
542
543#[derive(Debug, Clone, Default)]
545pub struct LocalizedContextLabel {
546 pub language: String,
547 pub text: String,
548}
549
550#[derive(Debug, Clone, Default)]
554pub struct ContextRecord {
555 pub slug: String,
556 pub wikidata: String,
557 pub aliases: Vec<String>,
558 pub labels: Vec<LocalizedContextLabel>,
559}
560
561impl ContextRecord {
562 #[must_use]
565 pub fn label_for(&self, language: &str) -> &str {
566 if let Some(label) = self.labels.iter().find(|l| l.language == language) {
567 return &label.text;
568 }
569 if let Some(label) = self.labels.iter().find(|l| l.language == "en") {
570 return &label.text;
571 }
572 &self.slug
573 }
574
575 #[must_use]
578 pub fn matches(&self, value: &str) -> bool {
579 let needle = value.trim().to_lowercase();
580 if needle.is_empty() {
581 return false;
582 }
583 if self
584 .aliases
585 .iter()
586 .any(|alias| alias.trim().to_lowercase() == needle)
587 {
588 return true;
589 }
590 self.labels
591 .iter()
592 .any(|label| label.text.trim().to_lowercase() == needle)
593 }
594}
595
596#[must_use]
597pub fn concept_contexts() -> Vec<ContextRecord> {
598 let tree = parse_lino(CONCEPT_CONTEXTS_LINO);
599 let mut out = Vec::new();
600 if let Some(root) = tree.children.first() {
601 for entry in root.children.iter().filter(|c| c.name == "context") {
602 let slug = entry.id.clone();
603 if slug.is_empty() {
604 continue;
605 }
606 let aliases = split_pipe_list(entry.find_child_value("aliases"));
607 let mut labels = Vec::new();
608 for child in entry.children.iter().filter(|c| c.name == "label") {
609 let lang = child.id.clone();
610 if lang.is_empty() {
611 continue;
612 }
613 labels.push(LocalizedContextLabel {
614 language: lang,
615 text: child.find_child_value("text").to_string(),
616 });
617 }
618 out.push(ContextRecord {
619 slug,
620 wikidata: entry.find_child_value("wikidata").to_string(),
621 aliases,
622 labels,
623 });
624 }
625 }
626 out
627}
628
629#[derive(Debug, Clone, Default)]
639pub struct IntentRoute {
640 pub id: String,
641 pub slug: String,
642 pub response_link: String,
643 pub keywords: Vec<String>,
644 pub phrases: Vec<String>,
645 pub tokens: Vec<String>,
646 pub combos: Vec<Vec<String>>,
647}
648
649#[derive(Debug, Clone, Default)]
650pub struct IntentRouting {
651 pub intents: Vec<IntentRoute>,
652 pub article_prefixes: Vec<String>,
653 pub trace_prefixes: Vec<String>,
654}
655
656#[must_use]
657pub fn intent_routing() -> IntentRouting {
658 let tree = parse_lino(INTENT_ROUTING_LINO);
659 let mut routing = IntentRouting::default();
660 if let Some(root) = tree.children.first() {
661 for child in &root.children {
662 match child.name.as_str() {
663 "intent" => {
664 let mut keywords = Vec::new();
665 let mut phrases = Vec::new();
666 let mut tokens = Vec::new();
667 let mut combos = Vec::new();
668 for entry in &child.children {
669 match entry.name.as_str() {
670 "keyword" => keywords.push(entry.id.clone()),
671 "phrase" => phrases.push(entry.id.clone()),
672 "token" => tokens.push(entry.id.clone()),
673 "combo" => combos.push(
674 entry
675 .id
676 .split('+')
677 .map(str::trim)
678 .filter(|s| !s.is_empty())
679 .map(ToOwned::to_owned)
680 .collect(),
681 ),
682 _ => {}
683 }
684 }
685 routing.intents.push(IntentRoute {
686 id: child.id.clone(),
687 slug: child.find_child_value("slug").to_string(),
688 response_link: child.find_child_value("response_link").to_string(),
689 keywords,
690 phrases,
691 tokens,
692 combos,
693 });
694 }
695 "article" => routing.article_prefixes.push(child.id.clone()),
696 "trace_prefix" => routing.trace_prefixes.push(child.id.clone()),
697 _ => {}
698 }
699 }
700 }
701 routing
702}
703
704#[derive(Debug, Clone, Default)]
712pub struct LearningSource {
713 pub id: String,
714 pub capability: String,
715 pub host: String,
716 pub keywords: Vec<String>,
717}
718
719#[derive(Debug, Clone, Default)]
722pub struct LearningSources {
723 pub sources: Vec<LearningSource>,
724 pub directive_cues: Vec<String>,
725}
726
727impl LearningSources {
728 #[must_use]
743 pub fn match_directive(&self, lowercased: &str) -> Option<&LearningSource> {
744 let has_cue = self
745 .directive_cues
746 .iter()
747 .any(|cue| lowercased.contains(cue.as_str()));
748 if !has_cue {
749 return None;
750 }
751 self.sources.iter().find(|source| {
752 (!source.host.is_empty() && lowercased.contains(source.host.as_str()))
753 || source
754 .keywords
755 .iter()
756 .any(|keyword| lowercased.contains(keyword.as_str()))
757 })
758 }
759}
760
761#[must_use]
767pub fn learning_sources() -> LearningSources {
768 let tree = parse_lino(LEARNING_SOURCES_LINO);
769 let mut registry = LearningSources::default();
770 if let Some(root) = tree.children.first() {
771 for child in &root.children {
772 match child.name.as_str() {
773 "source" => {
774 let keywords = child
775 .children
776 .iter()
777 .filter(|entry| entry.name == "keyword")
778 .map(|entry| entry.id.clone())
779 .collect();
780 registry.sources.push(LearningSource {
781 id: child.id.clone(),
782 capability: child.find_child_value("capability").to_string(),
783 host: child.find_child_value("host").to_string(),
784 keywords,
785 });
786 }
787 "directive" => {
788 for entry in child.children.iter().filter(|entry| entry.name == "cue") {
789 registry.directive_cues.push(entry.id.clone());
790 }
791 }
792 _ => {}
793 }
794 }
795 }
796 registry
797}
798
799#[must_use]
800pub fn prompt_patterns() -> Vec<PromptPattern> {
801 let tree = parse_lino(PROMPT_PATTERNS_LINO);
802 let mut out = Vec::new();
803 if let Some(root) = tree.children.first() {
804 for entry in root.children.iter().filter(|c| c.name == "pattern") {
805 let text = entry.find_child_value("text").to_string();
806 if text.is_empty() {
807 continue;
808 }
809 out.push(PromptPattern {
810 id: entry.id.clone(),
811 intent: entry.find_child_value("intent").to_string(),
812 language: entry.find_child_value("language").to_string(),
813 kind: entry.find_child_value("kind").to_string(),
814 text,
815 });
816 }
817 }
818 out
819}
820
821#[derive(Debug, Clone, Default)]
829pub struct EnvironmentRecord {
830 pub id: String,
831 pub label: String,
832 pub runtime: String,
833 pub seed_path: String,
834 pub memory_store: String,
835 pub memory_export_command: String,
836 pub bundle_export_command: String,
837 pub bundle_import_command: String,
838 pub start_command: String,
839 pub package_command: String,
840 pub tools: Vec<String>,
841}
842
843#[derive(Debug, Clone, Default)]
844pub struct MigrationFlow {
845 pub id: String,
846 pub description: String,
847 pub file_format: String,
848}
849
850#[derive(Debug, Clone, Default)]
851pub struct EnvironmentDirectory {
852 pub environments: Vec<EnvironmentRecord>,
853 pub migration_description: String,
854 pub flows: Vec<MigrationFlow>,
855}
856
857#[must_use]
858pub fn environment_directory() -> EnvironmentDirectory {
859 let tree = parse_lino(ENVIRONMENTS_LINO);
860 let mut directory = EnvironmentDirectory::default();
861 for root in &tree.children {
862 match root.name.as_str() {
863 "environments" => {
864 for entry in root.children.iter().filter(|c| c.name == "environment") {
865 let tools = split_pipe_list(entry.find_child_value("tools"));
866 directory.environments.push(EnvironmentRecord {
867 id: entry.id.clone(),
868 label: entry.find_child_value("label").to_string(),
869 runtime: entry.find_child_value("runtime").to_string(),
870 seed_path: entry.find_child_value("seed_path").to_string(),
871 memory_store: entry.find_child_value("memory_store").to_string(),
872 memory_export_command: entry
873 .find_child_value("memory_export_command")
874 .to_string(),
875 bundle_export_command: entry
876 .find_child_value("bundle_export_command")
877 .to_string(),
878 bundle_import_command: entry
879 .find_child_value("bundle_import_command")
880 .to_string(),
881 start_command: entry.find_child_value("start_command").to_string(),
882 package_command: entry.find_child_value("package_command").to_string(),
883 tools,
884 });
885 }
886 }
887 "migration" => {
888 directory.migration_description =
889 child_value_alias(root, "note", "description").to_string();
890 for entry in root.children.iter().filter(|c| c.name == "flow") {
891 directory.flows.push(MigrationFlow {
892 id: entry.id.clone(),
893 description: child_value_alias(entry, "note", "description").to_string(),
894 file_format: entry.find_child_value("file_format").to_string(),
895 });
896 }
897 }
898 _ => {}
899 }
900 }
901 directory
902}
903
904fn child_value_alias<'a>(node: &'a LinoNode, primary: &str, fallback: &str) -> &'a str {
905 let value = node.find_child_value(primary);
906 if value.is_empty() {
907 node.find_child_value(fallback)
908 } else {
909 value
910 }
911}
912
913#[must_use]
917pub fn environment_records() -> Vec<EnvironmentRecord> {
918 environment_directory().environments
919}