1pub(crate) use crate::coding::{
9 program_language_by_alias, program_spec, program_template_count, supported_program_languages,
10 supported_program_tasks, ExecutionStatus, ProgramExecution, ProgramSpec, PROGRAM_LANGUAGES,
11 WRITE_PROGRAM_INTENT,
12};
13
14use std::sync::OnceLock;
15
16use serde::{Deserialize, Serialize};
17
18use crate::coding::guidance::{program_explanation_section, program_test_instructions};
19use crate::engine_assistant_name::{
20 assistant_name_answer, chinese_assistant_name_answer, hindi_assistant_name_answer,
21 russian_assistant_name_answer, ASSISTANT_NAME_EXAMPLES,
22};
23pub(crate) use crate::engine_responses::{
24 assistant_free_time_answer, chinese_unknown_answer, farewell_answer, greeting_answer,
25 hindi_unknown_answer, identity_answer, russian_unknown_answer, unknown_answer,
26 unknown_language_fallback_answer, wellbeing_answer,
27};
28use crate::engine_responses::{
29 chinese_assistant_free_time_answer, chinese_courtesy_response_answer, chinese_farewell_answer,
30 chinese_greeting_answer, chinese_identity_answer, chinese_test_status_answer,
31 chinese_wellbeing_answer, courtesy_response_answer, hindi_assistant_free_time_answer,
32 hindi_courtesy_response_answer, hindi_farewell_answer, hindi_greeting_answer,
33 hindi_identity_answer, hindi_test_status_answer, hindi_wellbeing_answer,
34 russian_assistant_free_time_answer, russian_courtesy_response_answer, russian_farewell_answer,
35 russian_greeting_answer, russian_identity_answer, russian_test_status_answer,
36 russian_wellbeing_answer, test_status_answer, ASSISTANT_FREE_TIME_EXAMPLES,
37 COURTESY_RESPONSE_EXAMPLES, GREETING_EXAMPLES, IDENTITY_EXAMPLES, TEST_STATUS_EXAMPLES,
38 UNKNOWN_EXAMPLES,
39};
40use crate::event_log::EventLog;
41use crate::language::Language;
42use crate::links_format::{flatten_lino_value, format_lino_record};
43use crate::seed;
44
45pub const DEFAULT_MODEL: &str = "formal-ai";
46
47pub use crate::thinking::{
50 humanize_meta_identifier, naturalize_thinking_step, render_thinking_steps,
51 thinking_language_label, thinking_narrative, ThinkingStep,
52};
53
54#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
55pub struct SymbolicAnswer {
56 pub intent: String,
57 pub answer: String,
58 pub confidence: f32,
59 pub evidence_links: Vec<String>,
60 #[serde(default, skip_serializing_if = "Vec::is_empty")]
61 pub thinking_steps: Vec<ThinkingStep>,
62 pub links_notation: String,
63 #[serde(default, skip_serializing_if = "Option::is_none")]
64 pub execution_recipe: Option<Box<ExecutionRecipe>>,
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
69pub struct ExecutionRecipe {
70 pub language: String,
71 pub source: String,
72 pub path: String,
73 pub commands: Vec<String>,
74}
75
76#[derive(Debug, Default, Clone, Copy)]
77pub struct FormalAiEngine;
78
79impl FormalAiEngine {
80 #[must_use]
82 pub fn answer(&self, prompt: &str) -> SymbolicAnswer {
83 crate::solver::UniversalSolver::default().solve(prompt)
84 }
85}
86
87pub const KNOWLEDGE_SCHEMA_VERSION: &str = "0.2.0";
88
89#[must_use]
90pub fn knowledge_links_notation() -> String {
91 let mut records = vec![
92 format_lino_record(
93 "formal_ai_knowledge",
94 &[
95 ("model", String::from(DEFAULT_MODEL)),
96 ("schema_version", String::from(KNOWLEDGE_SCHEMA_VERSION)),
97 ("dataset_version", String::from(KNOWLEDGE_SCHEMA_VERSION)),
98 (
99 "policy",
100 String::from("deterministic symbolic rules; no neural network inference"),
101 ),
102 (
103 "format",
104 String::from(
105 "untyped indented Links Notation via lino-objects-codec format helpers",
106 ),
107 ),
108 ("rule_count", (1 + 7).to_string()),
109 ],
110 ),
111 format_concept_index_record(),
112 format_type_system_record(),
113 format_doublet_reduction_record(),
114 crate::skill_compiler::natural_language_skill_compiler_record(),
115 ];
116 records.extend(
117 crate::associative_package::default_associative_packages()
118 .into_iter()
119 .map(|package| package.links_notation()),
120 );
121 records.extend([
122 format_lino_record(
123 "rule_greeting",
124 &[
125 ("intent", String::from("greeting")),
126 ("response_link", String::from("response:greeting")),
127 ("answer", String::from(greeting_answer())),
128 ("examples", GREETING_EXAMPLES.join(", ")),
129 ("source", String::from("local symbolic seed set")),
130 ],
131 ),
132 format_lino_record(
133 "rule_courtesy_response",
134 &[
135 ("intent", String::from("courtesy_response")),
136 ("response_link", String::from("response:courtesy_response")),
137 ("answer", String::from(courtesy_response_answer())),
138 ("examples", COURTESY_RESPONSE_EXAMPLES.join(", ")),
139 ("source", String::from("local symbolic seed set")),
140 ],
141 ),
142 format_lino_record(
143 "rule_assistant_free_time",
144 &[
145 ("intent", String::from("assistant_free_time")),
146 (
147 "response_link",
148 String::from("response:assistant_free_time"),
149 ),
150 ("answer", String::from(assistant_free_time_answer())),
151 ("examples", ASSISTANT_FREE_TIME_EXAMPLES.join(", ")),
152 ("source", String::from("local symbolic seed set")),
153 ],
154 ),
155 format_lino_record(
156 "rule_identity",
157 &[
158 ("intent", String::from("identity")),
159 ("response_link", String::from("response:identity")),
160 ("answer", String::from(identity_answer())),
161 ("examples", IDENTITY_EXAMPLES.join(", ")),
162 ("source", String::from("local symbolic seed set")),
163 ],
164 ),
165 format_lino_record(
166 "rule_assistant_name",
167 &[
168 ("intent", String::from("assistant_name")),
169 ("response_link", String::from("response:assistant_name")),
170 ("answer", String::from(assistant_name_answer())),
171 ("examples", ASSISTANT_NAME_EXAMPLES.join(", ")),
172 ("source", String::from("local symbolic seed set")),
173 ],
174 ),
175 format_lino_record(
176 "rule_test_status",
177 &[
178 ("intent", String::from("test_status")),
179 ("response_link", String::from("response:test_status")),
180 ("answer", String::from(test_status_answer())),
181 ("examples", TEST_STATUS_EXAMPLES.join(", ")),
182 ("source", String::from("local symbolic seed set")),
183 ],
184 ),
185 ]);
186
187 records.push(format_write_program_rule_record());
188 records.push(format_lino_record(
189 "rule_unknown",
190 &[
191 ("intent", String::from("unknown")),
192 ("response_link", String::from("response:unknown")),
193 ("answer", String::from(unknown_answer())),
194 ("examples", UNKNOWN_EXAMPLES.join(", ")),
195 ("source", String::from("fallback symbolic rule")),
196 ],
197 ));
198
199 records.join("\n\n")
200}
201
202fn format_concept_index_record() -> String {
207 format_lino_record(
208 "concept_index",
209 &[
210 ("greeting", String::from("intent: greeting")),
211 ("test_status", String::from("intent: test_status")),
212 (
213 "courtesy_response",
214 String::from("intent: courtesy_response"),
215 ),
216 (
217 "assistant_free_time",
218 String::from("intent: assistant_free_time"),
219 ),
220 ("identity", String::from("intent: identity")),
221 ("assistant_name", String::from("intent: assistant_name")),
222 ("write_program", String::from("intent: write_program")),
223 ("translation", String::from("intent: translation")),
224 ("algorithm", String::from("intent: algorithm")),
225 ("meta_explanation", String::from("intent: meta_explanation")),
226 ("unknown", String::from("intent: unknown")),
227 ],
228 )
229}
230
231fn format_type_system_record() -> String {
234 format_lino_record(
235 "type_system",
236 &[
237 ("Type", String::from("Concept")),
238 ("SubType_intent", String::from("Concept -> Intent")),
239 ("SubType_language", String::from("Concept -> Language")),
240 ("SubType_source", String::from("Concept -> Source")),
241 ("SubType_program", String::from("Concept -> Program")),
242 ("SubType_meaning", String::from("Concept -> Meaning")),
243 ("SubType_trace", String::from("Concept -> Trace")),
244 (
245 "SubType_skill_package",
246 String::from("Concept -> CompiledSkillPackage"),
247 ),
248 (
249 "Value_example",
250 String::from("Type Concept; SubType Intent; Value greeting"),
251 ),
252 ],
253 )
254}
255
256fn format_doublet_reduction_record() -> String {
260 format_lino_record(
261 "doublet_reduction",
262 &[
263 ("doublets", String::from("from -> to pairs")),
264 ("from", String::from("any node id")),
265 ("to", String::from("any node id")),
266 (
267 "invariant",
268 String::from("every record reduces to doublets"),
269 ),
270 ],
271 )
272}
273
274#[must_use]
275pub fn estimate_tokens(text: &str) -> u32 {
276 u32::try_from(text.chars().count()).unwrap_or(u32::MAX)
277}
278
279#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
281pub struct GraphNode {
282 pub id: String,
283 pub label: String,
284 pub links_notation: String,
285}
286
287#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
289pub struct GraphEdge {
290 pub from: String,
291 pub to: String,
292 pub role: String,
293}
294
295#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
298pub struct KnowledgeGraph {
299 pub nodes: Vec<GraphNode>,
300 pub edges: Vec<GraphEdge>,
301}
302
303fn known_intent_slugs() -> &'static [String] {
304 static CELL: OnceLock<Vec<String>> = OnceLock::new();
305 CELL.get_or_init(|| {
306 seed::intent_routing()
307 .intents
308 .into_iter()
309 .filter_map(|route| {
310 if route.slug.is_empty() {
311 None
312 } else {
313 Some(route.slug)
314 }
315 })
316 .collect()
317 })
318 .as_slice()
319}
320
321fn trace_prefixes() -> &'static [String] {
322 static CELL: OnceLock<Vec<String>> = OnceLock::new();
323 CELL.get_or_init(|| seed::intent_routing().trace_prefixes)
324 .as_slice()
325}
326
327#[must_use]
328pub fn is_known_trace_id(trace: &str) -> bool {
329 if trace_prefixes()
330 .iter()
331 .any(|prefix| trace.starts_with(prefix.as_str()))
332 {
333 return true;
334 }
335 known_intent_slugs()
336 .iter()
337 .any(|slug| trace.eq_ignore_ascii_case(slug) || trace.contains(slug.as_str()))
338}
339
340#[must_use]
341pub fn knowledge_graph() -> KnowledgeGraph {
342 let mut nodes = vec![
343 GraphNode {
344 id: String::from("formal_ai_knowledge"),
345 label: String::from("formal-ai knowledge root"),
346 links_notation: String::from("formal_ai_knowledge"),
347 },
348 GraphNode {
349 id: String::from("rule_greeting"),
350 label: String::from("Greeting rule"),
351 links_notation: format!("rule_greeting answer={}", greeting_answer()),
352 },
353 GraphNode {
354 id: String::from("rule_identity"),
355 label: String::from("Identity rule"),
356 links_notation: format!("rule_identity answer={}", identity_answer()),
357 },
358 GraphNode {
359 id: String::from("rule_assistant_name"),
360 label: String::from("Assistant name rule"),
361 links_notation: format!("rule_assistant_name answer={}", assistant_name_answer()),
362 },
363 GraphNode {
364 id: String::from("rule_courtesy_response"),
365 label: String::from("Courtesy response rule"),
366 links_notation: format!(
367 "rule_courtesy_response answer={}",
368 courtesy_response_answer()
369 ),
370 },
371 GraphNode {
372 id: String::from("rule_assistant_free_time"),
373 label: String::from("Assistant free-time rule"),
374 links_notation: format!(
375 "rule_assistant_free_time answer={}",
376 assistant_free_time_answer()
377 ),
378 },
379 GraphNode {
380 id: String::from("rule_test_status"),
381 label: String::from("Test status rule"),
382 links_notation: format!("rule_test_status answer={}", test_status_answer()),
383 },
384 GraphNode {
385 id: String::from("rule_unknown"),
386 label: String::from("Unknown fallback rule"),
387 links_notation: format!("rule_unknown answer={}", unknown_answer()),
388 },
389 ];
390 let mut edges = vec![
391 GraphEdge {
392 from: String::from("formal_ai_knowledge"),
393 to: String::from("rule_greeting"),
394 role: String::from("contains"),
395 },
396 GraphEdge {
397 from: String::from("formal_ai_knowledge"),
398 to: String::from("rule_identity"),
399 role: String::from("contains"),
400 },
401 GraphEdge {
402 from: String::from("formal_ai_knowledge"),
403 to: String::from("rule_assistant_name"),
404 role: String::from("contains"),
405 },
406 GraphEdge {
407 from: String::from("formal_ai_knowledge"),
408 to: String::from("rule_courtesy_response"),
409 role: String::from("contains"),
410 },
411 GraphEdge {
412 from: String::from("formal_ai_knowledge"),
413 to: String::from("rule_assistant_free_time"),
414 role: String::from("contains"),
415 },
416 GraphEdge {
417 from: String::from("formal_ai_knowledge"),
418 to: String::from("rule_test_status"),
419 role: String::from("contains"),
420 },
421 GraphEdge {
422 from: String::from("formal_ai_knowledge"),
423 to: String::from("rule_unknown"),
424 role: String::from("contains"),
425 },
426 GraphEdge {
427 from: String::from("rule_greeting"),
428 to: String::from("response:greeting"),
429 role: String::from("response_link"),
430 },
431 GraphEdge {
432 from: String::from("rule_identity"),
433 to: String::from("response:identity"),
434 role: String::from("response_link"),
435 },
436 GraphEdge {
437 from: String::from("rule_assistant_name"),
438 to: String::from("response:assistant_name"),
439 role: String::from("response_link"),
440 },
441 GraphEdge {
442 from: String::from("rule_courtesy_response"),
443 to: String::from("response:courtesy_response"),
444 role: String::from("response_link"),
445 },
446 GraphEdge {
447 from: String::from("rule_assistant_free_time"),
448 to: String::from("response:assistant_free_time"),
449 role: String::from("response_link"),
450 },
451 GraphEdge {
452 from: String::from("rule_test_status"),
453 to: String::from("response:test_status"),
454 role: String::from("response_link"),
455 },
456 ];
457 nodes.push(GraphNode {
458 id: String::from("rule_write_program"),
459 label: String::from("Write-program rule"),
460 links_notation: format!(
461 "rule_write_program parameters=language,task languages={} tasks={}",
462 supported_program_languages(),
463 supported_program_tasks()
464 ),
465 });
466 edges.push(GraphEdge {
467 from: String::from("formal_ai_knowledge"),
468 to: String::from("rule_write_program"),
469 role: String::from("contains"),
470 });
471 edges.push(GraphEdge {
472 from: String::from("rule_write_program"),
473 to: String::from("response:write_program"),
474 role: String::from("response_link"),
475 });
476 let (package_nodes, package_edges) =
477 crate::associative_package::default_package_graph_projection();
478 nodes.extend(package_nodes);
479 edges.extend(package_edges);
480 KnowledgeGraph { nodes, edges }
481}
482
483#[must_use]
484pub fn knowledge_graph_dot() -> String {
485 use std::fmt::Write as _;
486 let graph = knowledge_graph();
487 let mut dot = String::from("digraph formal_ai_knowledge {\n");
488 for node in &graph.nodes {
489 let _ = writeln!(dot, " \"{}\" [label=\"{}\"];", node.id, node.label);
490 }
491 for edge in &graph.edges {
492 let _ = writeln!(
493 dot,
494 " \"{}\" -> \"{}\" [label=\"{}\"];",
495 edge.from, edge.to, edge.role
496 );
497 }
498 dot.push_str("}\n");
499 dot
500}
501
502#[must_use]
503pub fn stable_id(prefix: &str, text: &str) -> String {
504 crate::web_engine_core::stable_id(prefix, text)
505}
506
507pub(crate) enum SelectedRule {
508 Greeting,
509 Wellbeing,
510 Farewell,
511 TestStatus,
512 CourtesyResponse,
513 AssistantFreeTime,
514 Identity,
515 AssistantName,
516 WriteProgram(ProgramSpec),
517 UnsupportedWriteProgram {
518 task: Option<String>,
519 language: Option<String>,
520 },
521 Unknown,
522}
523
524impl SelectedRule {
525 pub(crate) fn intent(&self) -> String {
526 match self {
527 Self::Greeting => String::from("greeting"),
528 Self::Wellbeing => String::from("wellbeing"),
529 Self::Farewell => String::from("farewell"),
530 Self::TestStatus => String::from("test_status"),
531 Self::CourtesyResponse => String::from("courtesy_response"),
532 Self::AssistantFreeTime => String::from("assistant_free_time"),
533 Self::Identity => String::from("identity"),
534 Self::AssistantName => String::from("assistant_name"),
535 Self::WriteProgram(_) => String::from(WRITE_PROGRAM_INTENT),
536 Self::UnsupportedWriteProgram { .. } => String::from("write_program_unsupported"),
537 Self::Unknown => String::from("unknown"),
538 }
539 }
540
541 pub(crate) fn response_link(&self) -> String {
542 match self {
543 Self::Greeting => String::from("response:greeting"),
544 Self::Wellbeing => String::from("response:wellbeing"),
545 Self::Farewell => String::from("response:farewell"),
546 Self::TestStatus => String::from("response:test_status"),
547 Self::CourtesyResponse => String::from("response:courtesy_response"),
548 Self::AssistantFreeTime => String::from("response:assistant_free_time"),
549 Self::Identity => String::from("response:identity"),
550 Self::AssistantName => String::from("response:assistant_name"),
551 Self::WriteProgram(spec) => spec.response_link(),
552 Self::UnsupportedWriteProgram { .. } => {
553 String::from("response:write_program:unsupported")
554 }
555 Self::Unknown => String::from("response:unknown"),
556 }
557 }
558
559 pub(crate) fn answer(&self) -> String {
560 match self {
561 Self::Greeting => String::from(greeting_answer()),
562 Self::Wellbeing => String::from(wellbeing_answer()),
563 Self::Farewell => String::from(farewell_answer()),
564 Self::TestStatus => String::from(test_status_answer()),
565 Self::CourtesyResponse => String::from(courtesy_response_answer()),
566 Self::AssistantFreeTime => String::from(assistant_free_time_answer()),
567 Self::Identity => String::from(identity_answer()),
568 Self::AssistantName => String::from(assistant_name_answer()),
569 Self::WriteProgram(spec) => write_program_answer(*spec, Language::English, false),
570 Self::UnsupportedWriteProgram { task, language } => unsupported_write_program_answer(
571 task.as_deref(),
572 language.as_deref(),
573 Language::English,
574 ),
575 Self::Unknown => String::from(unknown_answer()),
576 }
577 }
578}
579
580pub(crate) fn language_aware_intent_for(rule: &SelectedRule, _language: Language) -> String {
581 rule.intent()
582}
583
584pub(crate) fn language_aware_answer_for(
585 rule: &SelectedRule,
586 language: Language,
587 prompt: &str,
588 prior_code_response: bool,
589) -> String {
590 match (rule, language) {
591 (SelectedRule::Greeting, Language::Russian) => String::from(russian_greeting_answer()),
592 (SelectedRule::Greeting, Language::Hindi) => String::from(hindi_greeting_answer()),
593 (SelectedRule::Greeting, Language::Chinese) => String::from(chinese_greeting_answer()),
594 (SelectedRule::Wellbeing, Language::Russian) => String::from(russian_wellbeing_answer()),
595 (SelectedRule::Wellbeing, Language::Hindi) => String::from(hindi_wellbeing_answer()),
596 (SelectedRule::Wellbeing, Language::Chinese) => String::from(chinese_wellbeing_answer()),
597 (SelectedRule::Farewell, Language::Russian) => String::from(russian_farewell_answer()),
598 (SelectedRule::Farewell, Language::Hindi) => String::from(hindi_farewell_answer()),
599 (SelectedRule::Farewell, Language::Chinese) => String::from(chinese_farewell_answer()),
600 (SelectedRule::TestStatus, Language::Russian) => String::from(russian_test_status_answer()),
601 (SelectedRule::TestStatus, Language::Hindi) => String::from(hindi_test_status_answer()),
602 (SelectedRule::TestStatus, Language::Chinese) => String::from(chinese_test_status_answer()),
603 (SelectedRule::CourtesyResponse, Language::Russian) => {
604 String::from(russian_courtesy_response_answer())
605 }
606 (SelectedRule::CourtesyResponse, Language::Hindi) => {
607 String::from(hindi_courtesy_response_answer())
608 }
609 (SelectedRule::CourtesyResponse, Language::Chinese) => {
610 String::from(chinese_courtesy_response_answer())
611 }
612 (SelectedRule::AssistantFreeTime, Language::Russian) => {
613 String::from(russian_assistant_free_time_answer())
614 }
615 (SelectedRule::AssistantFreeTime, Language::Hindi) => {
616 String::from(hindi_assistant_free_time_answer())
617 }
618 (SelectedRule::AssistantFreeTime, Language::Chinese) => {
619 String::from(chinese_assistant_free_time_answer())
620 }
621 (SelectedRule::Identity, Language::Russian) => String::from(russian_identity_answer()),
622 (SelectedRule::Identity, Language::Hindi) => String::from(hindi_identity_answer()),
623 (SelectedRule::Identity, Language::Chinese) => String::from(chinese_identity_answer()),
624 (SelectedRule::AssistantName, Language::Russian) => {
625 String::from(russian_assistant_name_answer())
626 }
627 (SelectedRule::AssistantName, Language::Hindi) => {
628 String::from(hindi_assistant_name_answer())
629 }
630 (SelectedRule::AssistantName, Language::Chinese) => {
631 String::from(chinese_assistant_name_answer())
632 }
633 (SelectedRule::WriteProgram(spec), language) => {
634 let answer = write_program_answer(*spec, language, prior_code_response);
635 crate::code_editing::apply_inline_hello_world_output_replacement(prompt, &answer, *spec)
636 .unwrap_or(answer)
637 }
638 (
639 SelectedRule::UnsupportedWriteProgram {
640 task,
641 language: program_language,
642 },
643 language,
644 ) => {
645 unsupported_write_program_answer(task.as_deref(), program_language.as_deref(), language)
646 }
647 (SelectedRule::Unknown, _) => {
648 crate::unknown_opener::language_aware_unknown_answer(prompt, language)
649 }
650 _ => rule.answer(),
651 }
652}
653
654pub(crate) fn response_link_for_intent(rule: &SelectedRule, _intent: &str) -> String {
655 rule.response_link()
656}
657
658pub(crate) fn hello_world_program_by_alias(normalized: &str) -> Option<ProgramSpec> {
660 let language = program_language_by_alias(normalized)?;
661 program_spec("hello_world", language.slug)
662}
663
664pub(crate) fn normalize_prompt(prompt: &str) -> String {
665 let canonical: String = prompt
666 .chars()
667 .flat_map(char::to_lowercase)
668 .collect::<String>()
669 .replace("c++", " cpp ")
670 .replace("c#", " csharp ");
671
672 let mut normalized = String::with_capacity(canonical.len());
673 for character in canonical.chars() {
674 if character.is_alphanumeric() || is_script_combining_mark(character) {
675 normalized.push(character);
676 } else {
677 normalized.push(' ');
678 }
679 }
680
681 normalized.split_whitespace().collect::<Vec<_>>().join(" ")
682}
683
684const fn is_script_combining_mark(character: char) -> bool {
685 let codepoint = character as u32;
686 matches!(
687 codepoint,
688 0x0300..=0x036F
689 | 0x0900..=0x094F
690 | 0x0951..=0x0957
691 | 0x0962..=0x0963
692 | 0x0980..=0x09FF
693 | 0x0A00..=0x0A7F
694 | 0x0A80..=0x0AFF
695 | 0x0B00..=0x0B7F
696 )
697}
698
699pub(crate) fn answer_links_notation(
700 prompt: &str,
701 intent: &str,
702 answer: &str,
703 log: &EventLog,
704 trace_id: &str,
705) -> String {
706 let steps = log
707 .events()
708 .iter()
709 .enumerate()
710 .map(|(index, event)| {
711 format!(
712 "step_{index} {} {}",
713 event.kind,
714 flatten_lino_value(&event.payload)
715 )
716 })
717 .collect::<Vec<_>>()
718 .join("; ");
719 let thinking_steps = log
720 .thinking_steps()
721 .iter()
722 .map(|step| {
723 format!(
724 "step_{} {} {} {} {}",
725 step.order,
726 flatten_lino_value(&step.step),
727 flatten_lino_value(&step.level),
728 flatten_lino_value(&step.source_event),
729 flatten_lino_value(&step.detail)
730 )
731 })
732 .collect::<Vec<_>>()
733 .join("; ");
734 format_lino_record(
735 &format!("answer_{}", stable_id("prompt", prompt)),
736 &[
737 ("prompt", String::from(prompt)),
738 ("intent", String::from(intent)),
739 ("answer", String::from(answer)),
740 ("trace", String::from(trace_id)),
741 ("steps", steps),
742 ("thinking_steps", thinking_steps),
743 ],
744 )
745}
746
747fn format_write_program_rule_record() -> String {
748 let sample = program_spec("hello_world", "rust").map_or_else(
749 || String::from("Write-program template catalog is unavailable."),
750 |spec| write_program_answer(spec, Language::English, false),
751 );
752 format_lino_record(
753 "rule_write_program",
754 &[
755 ("intent", String::from(WRITE_PROGRAM_INTENT)),
756 ("parameters", String::from("language, task")),
757 ("languages", supported_program_languages()),
758 ("tasks", supported_program_tasks()),
759 ("template_count", program_template_count().to_string()),
760 ("response_link", String::from("response:write_program")),
761 ("answer", sample),
762 (
763 "examples",
764 String::from(
765 "Write me hello world program in Rust; Write a Python program that counts to three",
766 ),
767 ),
768 ("source", program_template_sources()),
769 ],
770 )
771}
772
773fn program_template_sources() -> String {
774 let mut sources = Vec::new();
775 for language in PROGRAM_LANGUAGES {
776 if !sources.contains(&language.source) {
777 sources.push(language.source);
778 }
779 }
780 sources.join(", ")
781}
782
783fn write_program_answer(
784 spec: ProgramSpec,
785 language: Language,
786 prior_code_response: bool,
787) -> String {
788 let expected_output = spec.expected_output();
801 format!(
802 "{}\n\n```{}\n{}\n```\n\n{}\n\n{}\n\n{}",
803 write_program_intro(spec.language.name, spec.task.label, language),
804 spec.language.code_fence,
805 spec.template.code,
806 execution_report(&spec.language.execution, &expected_output, language),
807 program_explanation_section(spec, language),
808 program_test_instructions(spec, language, prior_code_response),
809 )
810}
811
812fn write_program_intro(language_name: &str, task_label: &str, language: Language) -> String {
813 match language {
814 Language::Russian => {
815 format!("Вот минимальная программа на языке {language_name} ({task_label}):")
816 }
817 Language::Hindi => {
818 format!("यहाँ {language_name} में एक न्यूनतम प्रोग्राम है ({task_label}):")
819 }
820 Language::Chinese => format!("这是一个最小的 {language_name} 程序({task_label}):"),
821 _ => format!("Here is a minimal {language_name} {task_label} program:"),
822 }
823}
824
825fn unsupported_write_program_answer(
826 task: Option<&str>,
827 language: Option<&str>,
828 response_language: Language,
829) -> String {
830 let task = task.unwrap_or("missing");
831 let language = language.unwrap_or("missing");
832 let languages = supported_program_languages();
833 let tasks = supported_program_tasks();
834 match response_language {
835 Language::Russian => format!(
836 "Я могу выполнить `write_program(language, task)`, но у меня нет шаблона для \
837 языка `{language}` и задачи `{task}`. Поддерживаемые языки: {languages}. \
838 Поддерживаемые задачи: {tasks}."
839 ),
840 Language::Hindi => format!(
841 "मैं `write_program(language, task)` रूट कर सकता हूँ, लेकिन भाषा `{language}` और \
842 कार्य `{task}` के लिए मेरे पास कोई टेम्पलेट नहीं है। समर्थित भाषाएँ: {languages}. \
843 समर्थित कार्य: {tasks}."
844 ),
845 Language::Chinese => format!(
846 "我可以路由 `write_program(language, task)`,但我没有语言 `{language}` 和任务 \
847 `{task}` 的模板。支持的语言:{languages}。支持的任务:{tasks}。"
848 ),
849 _ => format!(
850 "I can route `write_program(language, task)`, but I do not have a template for \
851 language `{language}` and task `{task}`. Supported languages: {languages}. \
852 Supported tasks: {tasks}."
853 ),
854 }
855}
856
857fn execution_report(execution: &ProgramExecution, output: &str, language: Language) -> String {
858 let command_lines = execution_command_lines(execution);
859 let verified = matches!(execution.status, ExecutionStatus::Verified);
860 let status_phrase = execution_status_phrase(execution.status, language);
861 let output_label = execution_output_label(verified, language);
862 let status_line = match language {
863 Language::Russian => format!(
864 "Статус выполнения: {status_phrase} в среде «{}».",
865 execution.environment
866 ),
867 Language::Hindi => format!(
868 "निष्पादन स्थिति: {status_phrase} ({} में)।",
869 execution.environment
870 ),
871 Language::Chinese => {
872 format!("执行状态:{status_phrase}({})。", execution.environment)
873 }
874 _ => format!(
875 "Execution status: {status_phrase} in {}.",
876 execution.environment
877 ),
878 };
879
880 format!(
881 "{status_line}\n{command_lines}\n{output_label}:\n```text\n{output}\n```\n{}",
882 execution.notes
883 )
884}
885
886const fn execution_status_phrase(status: ExecutionStatus, language: Language) -> &'static str {
887 match (status, language) {
888 (ExecutionStatus::Verified, Language::Russian) => "скомпилировано и запущено",
889 (ExecutionStatus::Verified, Language::Hindi) => "संकलित और चलाया गया",
890 (ExecutionStatus::Verified, Language::Chinese) => "已编译并运行",
891 (ExecutionStatus::Unavailable, Language::Russian) => "не скомпилировано и не запущено",
892 (ExecutionStatus::Unavailable, Language::Hindi) => "संकलित या चलाया नहीं गया",
893 (ExecutionStatus::Unavailable, Language::Chinese) => "未编译或运行",
894 (status, _) => status.label(),
895 }
896}
897
898const fn execution_output_label(verified: bool, language: Language) -> &'static str {
899 match (verified, language) {
900 (true, Language::Russian) => "Вывод",
901 (false, Language::Russian) => "Ожидаемый вывод после проверки",
902 (true, Language::Hindi) => "आउटपुट",
903 (false, Language::Hindi) => "सत्यापन के बाद अपेक्षित आउटपुट",
904 (true, Language::Chinese) => "输出",
905 (false, Language::Chinese) => "验证后的预期输出",
906 (true, _) => "Output",
907 (false, _) => "Expected output after verification",
908 }
909}
910
911fn execution_command_lines(execution: &ProgramExecution) -> String {
912 execution.check_command.map_or_else(
913 || format!("Run command: `{}`", execution.run_command),
914 |check_command| {
915 format!(
916 "Check command: `{check_command}`\nRun command: `{}`",
917 execution.run_command
918 )
919 },
920 )
921}