1"use strict";(globalThis.webpackChunkmellea_docs=globalThis.webpackChunkmellea_docs||[]).push([[23677],{80387(e,n,t){t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>c,default:()=>h,frontMatter:()=>o,metadata:()=>a,toc:()=>i});const a=JSON.parse('{"id":"advanced/mellea-core-internals","title":"Mellea Core Internals","description":"The three core data structures and abstraction layers underlying every Mellea program.","source":"@site/versioned_docs/version-0.7.0/advanced/mellea-core-internals.md","sourceDirName":"advanced","slug":"/advanced/mellea-core-internals","permalink":"/0.7.0/advanced/mellea-core-internals","draft":false,"unlisted":false,"editUrl":"https://github.com/generative-computing/mellea/edit/main/versioned_docs/version-0.7.0/advanced/mellea-core-internals.md","tags":[],"version":"0.7.0","lastUpdatedAt":1783970721000,"frontMatter":{"title":"Mellea Core Internals","description":"The three core data structures and abstraction layers underlying every Mellea program.","sidebar_label":"Core Internals"},"sidebar":"docsSidebar","previous":{"title":"Inference-Time Scaling","permalink":"/0.7.0/advanced/inference-time-scaling"},"next":{"title":"Template formatting","permalink":"/0.7.0/advanced/template-formatting"}}');var s=t(74848),r=t(28453);const o={title:"Mellea Core Internals",description:"The three core data structures and abstraction layers underlying every Mellea program.",sidebar_label:"Core Internals"},c=void 0,l={},i=[{value:"The three core data structures",id:"the-three-core-data-structures",level:2},{value:"<code>CBlock</code>",id:"cblock",level:3},{value:"<code>Component</code>",id:"component",level:3},{value:"<code>ModelOutputThunk</code>",id:"modeloutputthunk",level:3},{value:"The abstraction layers",id:"the-abstraction-layers",level:2},{value:"Layer 1: <code>MelleaSession</code>",id:"layer-1-melleasession",level:3},{value:"Layer 2: Functional API with explicit context",id:"layer-2-functional-api-with-explicit-context",level:3},{value:"Layer 3: Direct component construction with <code>mfuncs.act()</code>",id:"layer-3-direct-component-construction-with-mfuncsact",level:3},{value:"Layer 4: Async execution with <code>mfuncs.aact()</code>",id:"layer-4-async-execution-with-mfuncsaact",level:3},{value:"Layer 5: Lazy computation via <code>backend.generate_from_context()</code>",id:"layer-5-lazy-computation-via-backendgenerate_from_context",level:3},{value:"Layer 6: Composing lazy computations",id:"layer-6-composing-lazy-computations",level:3},{value:"Layer summary",id:"layer-summary",level:2},{value:"Template and prompt engineering",id:"template-and-prompt-engineering",level:2},{value:"TemplateFormatter",id:"templateformatter",level:3},{value:"<code>TemplateRepresentation</code>",id:"templaterepresentation",level:3},{value:"Customising templates for an existing class",id:"customising-templates-for-an-existing-class",level:3}];function d(e){const n={a:"a",blockquote:"blockquote",code:"code",em:"em",h2:"h2",h3:"h3",hr:"hr",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,r.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)(n.blockquote,{children:["\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Advanced:"})," This page is for contributors, backend developers, and anyone who\nwants to understand what happens when Mellea executes a request. If you are\nbuilding applications with Mellea, you do not need this material."]}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:["Mellea's high-level API (",(0,s.jsx)(n.code,{children:"m.chat()"}),", ",(0,s.jsx)(n.code,{children:"m.instruct()"}),", ",(0,s.jsx)(n.code,{children:"@generative"}),") is built on three\ncore data structures. Understanding these structures and the abstraction layers above\nthem explains how Mellea achieves lazy evaluation, parallel dispatch, and composable\ncontext management."]}),"\n",(0,s.jsx)(n.h2,{id:"the-three-core-data-structures",children:"The three core data structures"}),"\n",(0,s.jsx)(n.h3,{id:"cblock",children:(0,s.jsx)(n.code,{children:"CBlock"})}),"\n",(0,s.jsxs)(n.p,{children:["A ",(0,s.jsx)(n.code,{children:"CBlock"})," (content block) is a wrapper around a string that marks a tokenisation\nand KV caching boundary:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-python",children:'from mellea.core import CBlock\n\nblock = CBlock("What is 1+1?")\n'})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"CBlock"}),"s are the leaf nodes of every data dependency graph in Mellea. Importantly,\n",(0,s.jsx)(n.code,{children:"CBlock"})," boundaries affect tokenisation:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:"tokenise(CBlock(a) + CBlock(b)) == tokenise(a
1) + tokenise(b)\n"})}),"\n",(0,s.jsxs)(n.p,{children:["This may differ from ",(0,s.jsx)(n.code,{children:"tokenise(a + b)"}),". When you care about KV cache reuse, CBlock\nboundaries let you control exactly where the tokeniser makes splits."]}),"\n",(0,s.jsx)(n.h3,{id:"component",children:(0,s.jsx)(n.code,{children:"Component"})}),"\n",(0,s.jsxs)(n.p,{children:["A ",(0,s.jsx)(n.code,{children:"Component"})," is a declarative structure that can depend on other ",(0,s.jsx)(n.code,{children:"Component"}),"s or\n",(0,s.jsx)(n.code,{children:"CBlock"}),"s. Components are the unit of composition in Mellea. ",(0,s.jsx)(n.code,{children:"Message"}),",\n",(0,s.jsx)(n.a,{href:"../reference/glossary#instruction",children:(0,s.jsx)(n.code,{children:"Instruction"})}),", ",(0,s.jsx)(n.code,{children:"@mify"})," objects, and ",(0,s.jsx)(n.code,{children:"@generative"})," functions all produce ",(0,s.jsx)(n.code,{children:"Component"}),"s."]}),"\n",(0,s.jsx)(n.h3,{id:"modeloutputthunk",children:(0,s.jsx)(n.code,{children:"ModelOutputThunk"})}),"\n",(0,s.jsxs)(n.p,{children:["A ",(0,s.jsx)(n.code,{children:"ModelOutputThunk"})," is a lazy reference to a computation result. It represents the\n",(0,s.jsx)(n.em,{children:"future"})," output of an LLM call \u2014 the call may or may not have been dispatched yet\nwhen you receive the thunk. You can pass a thunk as an input to another ",(0,s.jsx)(n.code,{children:"Component"}),"\nbefore the underlying computation has completed."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-python",children:"thunk.is_computed() # True if the value is already available\nawait thunk.avalue() # Force evaluation; returns the actual value\n"})}),"\n",(0,s.jsx)(n.p,{children:"This lazy evaluation model lets the backend see the full dependency graph of a\nrequest before executing anything, enabling batching and optimisation."}),"\n",(0,s.jsx)(n.h2,{id:"the-abstraction-layers",children:"The abstraction layers"}),"\n",(0,s.jsx)(n.p,{children:"Each layer below is a thinner wrapper around the one beneath it. You work at\nwhatever level of abstraction the task requires."}),"\n",(0,s.jsxs)(n.h3,{id:"layer-1-melleasession",children:["Layer 1: ",(0,s.jsx)(n.code,{children:"MelleaSession"})]}),"\n",(0,s.jsx)(n.p,{children:"The entry point for most programs. The session bundles a backend, a context, and\nhigh-level methods. Everything is handled for you:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-python",children:'from mellea import MelleaSession\nfrom mellea.backends.ollama import OllamaModelBackend\nfrom mellea.stdlib.context import SimpleContext\n\nm = MelleaSession(backend=OllamaModelBackend("granite4:latest"), ctx=SimpleContext())\nresponse = m.chat("What is 1+1?")\nprint(response.content)\n'})}),"\n",(0,s.jsxs)(n.p,{children:["When you call ",(0,s.jsx)(n.code,{children:"m.chat()"}),", the session:"]}),"\n",(0,s.jsxs)(n.ol,{children:["\n",(0,s.jsxs)(n.li,{children:["Wraps your string in a ",(0,s.jsx)(n.code,{children:"Message"})," component"]}),"\n",(0,s.jsx)(n.li,{children:"Passes the component and context to the backend"}),"\n",(0,s.jsx)(n.li,{children:"Updates the context with the result"}),"\n",(0,s.jsxs)(n.li,{children:["Returns the response as a ",(0,s.jsx)(n.code,{children:"Message"})]}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"layer-2-functional-api-with-explicit-context",children:"Layer 2: Functional API with explicit context"}),"\n",(0,s.jsxs)(n.p,{children:["The functional API (",(0,s.jsx)(n.code,{children:"mfuncs"}),") exposes the same operations as stateless functions.\nContext is threaded explicitly \u2014 you pass it in and get a new context back:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-python",children:'import mellea.stdlib.functional as mfuncs\nfrom mellea.backends.ollama import OllamaModelBackend\nfrom mellea.stdlib.context import SimpleContext\n\nresponse, next_context = mfuncs.chat(\n "What is 1+1?",\n context=SimpleContext(),\n backend=OllamaModelBackend("granite4:latest"),\n)\nprint(response.content)\n'})}),"\n",(0,s.jsx)(n.p,{children:"This is useful when you need to fork, merge, or snapshot context explicitly."}),"\n",(0,s.jsxs)(n.h3,{id:"layer-3-direct-component-construction-with-mfuncsact",children:["Layer 3: Direct component construction with ",(0,s.jsx)(n.code,{children:"mfuncs.act()"})]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"mfuncs.act()"})," accepts any component or ",(0,s.jsx)(n.code,{children:"CBlock"})," directly. All other ",(0,s.jsx)(n.code,{children:"mfuncs"}),"\nfunctions (",(0,s.jsx)(n.code,{children:"chat"}),", ",(0,s.jsx)(n.code,{children:"instruct"}),", etc.) are thin wrappers that construct a component\nand then call ",(0,s.jsx)(n.code,{children:"act()"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-python",children:'import mellea.stdlib.functional as mfuncs\nfrom mellea.backends.ollama import OllamaModelBackend\nfrom mellea.stdlib.components import Instruction\nfrom mellea.stdlib.context import SimpleContext\n\nresponse, next_context = mfuncs.act(\n action=Instruction("What is 1+1?"),\n context=SimpleContext(),\n backend=OllamaModelBackend("granite4:latest"),\n)\nprint(response.value)\n'})}),"\n",(0,s.jsxs)(n.h3,{id:"layer-4-async-execution-with-mfuncsaact",children:["Layer 4: Async execution with ",(0,s.jsx)(n.code,{children:"mfuncs.aact(
1)"})]}),"\n",(0,s.jsxs)(n.p,{children:["Mellea's core is async. The synchronous API wraps the async operations with\n",(0,s.jsx)(n.code,{children:"asyncio.run()"}),". For each method in ",(0,s.jsx)(n.code,{children:"mfuncs"})," there is an ",(0,s.jsx)(n.code,{children:"a*"})," async version:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-python",children:'import asyncio\nimport mellea.stdlib.functional as mfuncs\nfrom mellea.backends.ollama import OllamaModelBackend\nfrom mellea.stdlib.components import Instruction\nfrom mellea.stdlib.context import SimpleContext\n\nasync def main():\n response, _ = await mfuncs.aact(\n Instruction("What is 1+1?"),\n context=SimpleContext(),\n backend=OllamaModelBackend("granite4:latest"),\n )\n print(response.value)\n\nasyncio.run(main())\n'})}),"\n",(0,s.jsxs)(n.h3,{id:"layer-5-lazy-computation-via-backendgenerate_from_context",children:["Layer 5: Lazy computation via ",(0,s.jsx)(n.code,{children:"backend.generate_from_context()"})]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"mfuncs.aact()"})," is itself a convenience wrapper around the backend's\n",(0,s.jsx)(n.code,{children:"generate_from_context()"})," method. Calling it directly returns a ",(0,s.jsx)(n.code,{children:"ModelOutputThunk"}),"\nrather than an evaluated response:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-python",children:'import asyncio\nfrom mellea.backends.ollama import OllamaModelBackend\nfrom mellea.core import CBlock\nfrom mellea.stdlib.context import SimpleContext\n\nasync def main():\n backend = OllamaModelBackend("granite4:latest")\n ctx = SimpleContext()\n\n response, _ = await backend.generate_from_context(CBlock("What is 1+1?"), ctx=ctx)\n\n print(f"Computed: {response.is_computed()}") # may be False\n print(await response.avalue()) # forces evaluation\n print(f"Computed: {response.is_computed()}") # True\n\nasyncio.run(main())\n'})}),"\n",(0,s.jsx)(n.h3,{id:"layer-6-composing-lazy-computations",children:"Layer 6: Composing lazy computations"}),"\n",(0,s.jsxs)(n.p,{children:["Because thunks are lazy, you can pass a thunk as an input to a second computation\n",(0,s.jsx)(n.em,{children:"before"})," the first one has been evaluated. This lets the backend optimise across\nthe full dependency graph:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-python",children:'import asyncio\nfrom mellea.backends.ollama import OllamaModelBackend\nfrom mellea.core import Backend, CBlock, Context\nfrom mellea.stdlib.components import SimpleComponent\nfrom mellea.stdlib.context import SimpleContext\n\nasync def main(backend: Backend, ctx: Context):\n x, _ = await backend.generate_from_context(CBlock("What is 1+1?"), ctx=ctx)\n y, _ = await backend.generate_from_context(CBlock("What is 2+2?"), ctx=ctx)\n\n # x and y may not have been computed yet \u2014 we can still use them as inputs\n z, _ = await backend.generate_from_context(\n SimpleComponent(instruction="What is x+y?", x=x, y=y),\n ctx=ctx,\n )\n\n print(f"x computed: {x.is_computed()}")\n print(f"y computed: {y.is_computed()}")\n print(await z.avalue()) # forces evaluation of the whole graph\n\nasyncio.run(main(OllamaModelBackend("granite4:latest"), SimpleContext()))\n'})}),"\n",(0,s.jsxs)(n.p,{children:["The backend sees ",(0,s.jsx)(n.code,{children:"z"}),"'s dependency on ",(0,s.jsx)(n.code,{children:"x"})," and ",(0,s.jsx)(n.code,{children:"y"}),", evaluates them in order (or\nin parallel if the backend supports it), and returns ",(0,s.jsx)(n.code,{children:"z"}),"'s result."]}),"\n",(0,s.jsx)(n.h2,{id:"layer-summary",children:"Layer summary"}),"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Layer"}),(0,s.jsx)(n.th,{children:"Entry point"}),(0,s.jsx)(n.th,{children:"Who uses it"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"MelleaSession"})}),(0,s.jsxs)(n.td,{children:[(0,s.jsx)(n.code,{children:"m.chat()"}),", ",(0,s.jsx)(n.code,{children:"m.instruct()"})]}),(0,s.jsx)(n.td,{children:"Application developers"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsxs)(n.td,{children:[(0,s.jsx)(n.code,{children:"mfuncs"})," synchronous"]}),(0,s.jsxs)(n.td,{children:[(0,s.jsx)(n.code,{children:"mfuncs.chat()"}),", ",(0,s.jsx)(n.code,{children:"mfuncs.act()"})]}),(0,s.jsx)(n.td,{children:"Application developers needing context control"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsxs)(n.td,{children:[(0,s.jsx)(n.code,{children:"mfuncs"})," async"]}),(0,s.jsxs)(n.td,{children:[(0,s.jsx)(n.code,{children:"mfuncs.aact(
1)"}),", ",(0,s.jsx)(n.code,{children:"mfuncs.achat()"})]}),(0,s.jsx)(n.td,{children:"Advanced users building async pipelines"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"backend.generate_from_context()"})}),(0,s.jsxs)(n.td,{children:["Thunks, ",(0,s.jsx)(n.code,{children:"is_computed()"}),", ",(0,s.jsx)(n.code,{children:"avalue()"})]}),(0,s.jsx)(n.td,{children:"Backend developers, advanced users"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:"Composition"}),(0,s.jsxs)(n.td,{children:[(0,s.jsx)(n.code,{children:"SimpleComponent"})," with thunk inputs"]}),(0,s.jsx)(n.td,{children:"Backend developers"})]})]})]}),"\n",(0,s.jsx)(n.h2,{id:"template-and-prompt-engineering",children:"Template and prompt engineering"}),"\n",(0,s.jsx)(n.h3,{id:"templateformatter",children:"TemplateFormatter"}),"\n",(0,s.jsxs)(n.p,{children:["Mellea formats Python objects into LLM-readable text using a ",(0,s.jsx)(n.a,{href:"../reference/glossary#templateformatter",children:(0,s.jsx)(n.code,{children:"TemplateFormatter"})}),".\nIt uses Jinja2 templates stored in a ",(0,s.jsx)(n.code,{children:"templates/prompts/"})," directory. Each\ncomponent class can have its own template, looked up by class name."]}),"\n",(0,s.jsx)(n.p,{children:"The formatter resolves templates in this order:"}),"\n",(0,s.jsxs)(n.ol,{children:["\n",(0,s.jsx)(n.li,{children:"Cached templates (from recent lookups)"}),"\n",(0,s.jsx)(n.li,{children:"The formatter's configured template path"}),"\n",(0,s.jsxs)(n.li,{children:["The package that owns the component (",(0,s.jsx)(n.code,{children:"mellea"})," or a third-party package)"]}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:["Within a template path, the formatter traverses subdirectories matching the model\nID before falling back to ",(0,s.jsx)(n.code,{children:"default/"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:"templates/prompts/\n\u251c\u2500\u2500 default/\n\u2502 \u2514\u2500\u2500 Instruction.jinja2 \u2190 fallback for all models\n\u2514\u2500\u2500 granite/\n \u2514\u2500\u2500 granite-3-2/\n \u2514\u2500\u2500 instruct/\n \u2514\u2500\u2500 Instruction.jinja2 \u2190 used for ibm-granite/granite-3.2-8b-instruct\n"})}),"\n",(0,s.jsxs)(n.p,{children:["The formatter returns the template from the deepest matching directory. A model ID\nof ",(0,s.jsx)(n.code,{children:"ibm-granite/granite-3.2-8b-instruct"})," matches ",(0,s.jsx)(n.code,{children:"granite/granite-3-2/instruct"}),"\nbut not ",(0,s.jsx)(n.code,{children:"ibm/"})," \u2014 only one path should match in any given templates directory."]}),"\n",(0,s.jsx)(n.h3,{id:"templaterepresentation",children:(0,s.jsx)(n.a,{href:"../reference/glossary#templaterepresentation",children:(0,s.jsx)(n.code,{children:"TemplateRepresentation"})})}),"\n",(0,s.jsxs)(n.p,{children:["Each component's ",(0,s.jsx)(n.code,{children:"format_for_llm()"})," method returns either a string or a\n",(0,s.jsx)(n.code,{children:"TemplateRepresentation"}),". The ",(0,s.jsx)(n.code,{children:"TemplateRepresentation"})," specifies:"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"A reference to the component instance"}),"\n",(0,s.jsx)(n.li,{children:"A dictionary of arguments passed to the template renderer"}),"\n",(0,s.jsx)(n.li,{children:"A list of tools or functions related to the component"}),"\n",(0,s.jsxs)(n.li,{children:["Either a ",(0,s.jsx)(n.code,{children:"template"})," (inline Jinja2 string) or a ",(0,s.jsx)(n.code,{children:"template_order"})," (list of\ntemplate file names to look up, where ",(0,s.jsx)(n.code,{children:"*"})," means the class name)"]}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"The simplest approach is to return a string directly \u2014 this bypasses templating\nentirely:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-python",children:'def format_for_llm(self) -> str:\n return f"Summarise: {self.text}"\n'})}),"\n",(0,s.jsx)(n.h3,{id:"customising-templates-for-an-existing-class",children:"Customising templates for an existing class"}),"\n",(0,s.jsxs)(n.p,{children:["To change how an existing component is rendered, subclass it and override\n",(0,s.jsx)(n.code,{children:"format_for_llm()"}),". Then create a new template file at the appropriate path.\nSee ",(0,s.jsx)(n.a,{href:"https://github.com/generative-computing/mellea/blob/main/docs/examples/mify/rich_document_advanced.py",children:(0,s.jsx)(n.code,{children:"docs/examples/mify/rich_document_advanced.py"})}),"\nfor a worked example."]}),"\n",(0,s.jsx)(n.hr,{}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"See also:"}),"\n",(0,s.jsx)(n.a,{href:"../concepts/generative-programming",children:"Generative Programming"})," |\n",(0,s.jsx)(n.a,{href:"../how-to/working-with-data",children:"Working with Data"})," |\n",(0,s.jsx)(n.a,{href:"../how-to/use-async-and-streaming",children:"Async and Streaming"})]})]})}function h(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(d,{...e})}):d(e)}},28453(e,n,t){t.d(n,{R:()=>o,x:()=>c});var a=t(96540);const s={},r=a.createContext(s);function o(e){const n=a.useContext(r);return a.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:o(e.components),a.createElement(r.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.