Elisa Beshero-Bondar, Molly Scott Wright, and Michael Simons

Penn State Erie, The Behrend College: DIGIT DH Lab

the separation of concerns
Let me try to explain to you, what to my taste is characteristic for all intelligent thinking. It is, that one is willing to study in depth an aspect of one’s subject matter in isolation for the sake of its own consistency, all the time knowing that one is occupying oneself only with one of the aspects.
On the Role of Scientific Thought(1974)
the separation of concerns
We know that a program must be correct and we can study it from that viewpoint only; we also know that it should be efficient and we can study its efficiency on another day, so to speak. [. . .] But nothing is gained —on the contrary!— by tackling these various aspects simultaneously. It is what I sometimes have calledthe separation of concerns, which, even if not perfectly possible, is yet the only available technique for effective ordering of one’s thoughts, that I know of.
On the Role of Scientific Thought(1974)
the separation of concerns
This is what I mean byfocussing one’s attention upon some aspect: it does not mean ignoring the other aspects, it is just doing justice to the fact that from this aspect’s point of view, the other is irrelevant. It is being one- and multiple-track minded simultaneously.
On the Role of Scientific Thought(1974)
separation of concernstoday
worldof an XML project, deploying XML-stack tools?
the separation of concerns: project XML to be addressed as XML, and not baked into the vector embedding systems of language model.
Definition: A standard used to connect models to external services, including:
MCP = a protocol to turn a language model into an agent that applies, executes, and adapts scripts.
hackaccess to external company software,
separation of concerns: Big agents are too broadly configured, needy because the tools in their containers are insufficient, therefore solve problems by escaping.
separation of concerns
<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="../schemas/recipe.rnc" type="application/relax-ng-compact-syntax"?>
<recipe><healthData>About this recipe Healthiness : (71 votes)</healthData>
<!--ebb: Hi This is a comment -->
<!--ebb 2020-08-27: I like putting initials on my comments, and sometimes the date,
especially if multiple people are writing comments on a code file. -->
<para>The <culture ref="England" monarch="George_III">Georgians</culture> loved rich, sweet food. Sugar had become much
more easily available (mostly because of the Transatlantic Slave Trade) and was fast
replacing honey as the main food sweetener.</para>
<para>This version of the syllabub recipe was by <name type="person">Eliza Acton</name>, who
lived in <timePeriod>18th century</timePeriod>. There are <altRecipe>plenty of earlier
versions but they are more likely to curdle as they contain cider</altRecipe>. This
version was very modern and fashionable in its day and is easy to manage: <blockQuote>Take a
quart of cream, a pint of sack, juice of a lemon, whip it, as the froth flies take it
off with a spoon and lay it in glasses: but first you must sweeten and stir some white
wine into your glasses, and gently lay on your froth. Set them by and do not make them
long before you use them.</blockQuote>
</para>
<para>Sugar and sherry (known as sack) were still expensive ingredients and dishes like this
would have been eaten in the houses of the richer merchants who would be able to afford
sugar, lemons, new salad vegetables and sack.</para>
<para>We have added a non-alcoholic lemon syllabub for you to try too.</para>
<para>For images of the cooking process see our Syllabub Pictures.</para>
<para type="credits">With thanks to <name type="person">Ian Pycroft</name> of <name
type="organization">Black Knight Historical</name> and to <name type="organization">The
Georgian House, <name type="place">Bristol</name></name>.</para>
<section kind="ingred">
<heading>Ingredients</heading>
<list><it quant="1" unit="fruit">1 <ingred>lemon</ingred></it>
<item quant=".25" unit="pint">1/4 pint <ingred>sack</ingred> (pale or dark)</item>
<item qant="2 3" unit="ounce">2-3 oz <ingred>caster
sugar</ingred></item><!-- This is one way to handle multiple good values in an attribugte,
with quant="2 3": It means quant can be 2 or 3.-->
<item quant=".5" unit="pint">1/2 pint <ingred>double cream</ingred></item>
<item quantLow="4" quantHigh="6" unit="Tb">4-6 tablespoons <ingred>sweet/dessert white
wine</ingred>
</item>
</list>
</section>
<section kind="equip">
<heading>Equipment</heading>
<list>
<item>Knife</item>
<item>Grater</item>
<item>Chopping board</item>
<item>Mixing bowl</item>
<item>Jug</item>
<item>Tablespoon</item>
</list>
</section>
<section kind="process">
<heading>Making and cooking it</heading>
<listStep>
<step num="1">Always <action>wash</action> your hands before preparing food</step>
<step num="2"><action>Grate</action> half the peel, <action>pare off</action> the rest
in fine strips</step>
<step num="3">Place sherry, grated peel, lemon juice and sugar in bowl and
<action>soak</action> for 2 hours</step>
<step num="4"><action>Whip</action> the cream until semi-stiff</step>
<step num="5"><action>Add</action> sherry gradually</step>
<step num="6"><action>Spoon</action> a little wine into glass and spoon on whipped
cream</step>
<step><action>Decorate</action> the top with lemon peel sticks</step>
<step num="8"><action>Serve</action> with Shrewsberry cakes</step>
</listStep>
</section>
</recipe>
This is purposefully uneven, rookie
student code for testing purposes.
start = recipe
recipe = element recipe {healthData, para+, section+}
healthData = element healthData {text}
para = element para {type?, mixed{ (culture | name | timePeriod
| altRecipe | blockQuote )*}}
culture = element culture {ref?, monarch?, text }
ref = attribute ref {text}
monarch = attribute monarch {text}
name = element name {type, mixed{name*}}
type= attribute type {"person" | "place" | "organization"
| "credits"}
timePeriod = element timePeriod {text}
altRecipe = element altRecipe {text}
blockQuote = element blockQuote {text}
section = element section {kind, heading, (\list | listStep) }
kind = attribute kind { "ingred" | "equip" | "process"}
heading = element heading {text}
\list = element list {item+}
listStep = element listStep {step+}
item = element item {quant?, quantLow?, quantHigh?, unit?,
mixed{(ingred)*}}
ingred = element ingred {text}
quant = attribute quant {list{xsd:float+}}
quantLow = attribute quantLow {xsd:float}
quantHigh = attribute quantHigh {xsd:float}
unit = attribute unit {"fruit" | "pint" | "ounce"
| "Tb" | "t" | text}
step = element step {num, mixed{(action | ingred)*}}
num = attribute num {xsd:int}
action = element action { text}
try:
if name == "xpath_query":
result = self.saxon_server.xpath_query(
xpath=arguments["xpath"],
version=arguments.get("version", "3.1"),
return_count=arguments.get("return_count", False)
)
elif name == "xquery_query":
result = self.saxon_server.xquery_query(
xquery=arguments["xquery"],
external_vars=arguments.get("external_vars")
)
elif name == "xslt_transform":
result = self.saxon_server.xslt_transform(
xslt=arguments["xslt"],
params=arguments.get("params"),
save_output=arguments.get("save_output")
)
elif name == "get_structure_summary":
result = self.saxon_server.get_structure_summary(
max_depth=arguments.get("max_depth", 3)
)
elif name == "find_irregularities":
result = self.saxon_server.find_irregularities(
checks=arguments["checks"]
)
elif name == "apply_transformation":
result = self.saxon_server.apply_transformation(
xslt=arguments["xslt"],
validate=arguments.get("validate", True),
description=arguments.get("description", "")
)
elif name == "batch_corrections":
result = self.saxon_server.batch_corrections(
corrections=arguments["corrections"],
validate=arguments.get("validate", True)
)
elif name == "create_backup":
backup_path = self.saxon_server.create_backup()
result = {
"success": True,
"backup_path": backup_path
}
elif name == "reload_document":
self.saxon_server.reload_document()
result = {
"success": True,
"message": "Document reloaded"
}
elif name == "list_available_documents":
result = self.saxon_server.list_available_documents()
elif name == "load_document":
result = self.saxon_server.load_document(
filename=arguments["filename"]
)
elif name == "list_available_schemas":
result = self.saxon_server.list_available_schemas()
else:
result = {
"success": False,
"error": f"Unknown tool: {name}"
}
We provide a basic "stub" of code for an XSLT 3.0 identity transformation. Review the Jing + Saxon tool scripts on our Codeberg repo at saxon_mcp_server.py
def _generate_correction_xslt(self, corrections: list[dict]) -> str:
"""Generate XSLT 3.0 stylesheet from correction operations"""
templates = []
for i, correction in enumerate(corrections):
op = correction.get("operation")
if op == "update":
xpath = correction["xpath"]
updates = correction["updates"]
# Generate template for update
template = f"""
<xsl:template match="{xpath}" priority="{i + 10}">
<xsl:copy>
<xsl:copy-of select="@*"/>
"""
if "attributes" in updates:
for attr, value in updates["attributes"].items():
template += f'<xsl:attribute name="{attr}">{value}</xsl:attribute>\n'
if "text" in updates:
template += f'<xsl:text>{updates["text"]}</xsl:text>\n'
else:
template += f'<xsl:apply-templates select="node()"/>\n'
template += """</xsl:copy>
</xsl:template>
"""
templates.append(template)
elif op == "delete":
xpath = correction["xpath"]
templates.append(f'<xsl:template match="{xpath}"/>\n')
# Build complete stylesheet
xslt = f"""<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:mode on-no-match="shallow copy"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<!-- Correction templates -->
{''.join(templates)}
</xsl:stylesheet>
"""
return xslt
MODELS = [
{
"label": "Qwen 3.6 (27B)",
"id": "qwen3.6:27b"
},
{
"label": "Qwen 3 Coder",
"id": "qwen3-coder:latest",
}
,
{
"label": "Qwen 2.5 (1.5B)",
"id": "qwen2.5:1.5b",
},
{
"label": "Qwen 3.5 (9B)",
"id": "qwen3.5:9b",
},
{
"label": "SmolLM3",
"id": "smollm3-tools:latest",
}
]
def select_model(default_id: str) -> dict:
"""Prompt the user to choose a model. `default_id` is used when the
user just presses Enter. Returns the selected model dict."""
default_index = next(
(i for i, m in enumerate(MODELS) if m["id"] == default_id),
None
)
if default_index is None:
print(f"{COLOR_ERROR}Warning:
default model '{default_id}' not in MODELS list
— falling back to first entry.{RESET}")
default_index = 0
print(f"\n{BOLD}{COLOR_HEADER}Available models:{RESET}\n")
for i, m in enumerate(MODELS):
print(f" {COLOR_LABEL}{i + 1}.{RESET} {m['label']}")
print(f" {DIM}{m['id']}{RESET}")
print()
while True:
raw = input(
f"{COLOR_LABEL}Choose a model [1–{len(MODELS)}, "
f"or Enter for default ({DEFAULT_MODEL_INDEX + 1})]: {RESET}"
).strip()
if not raw:
chosen = MODELS[DEFAULT_MODEL_INDEX]
if ensure_model_available(chosen["id"]):
print(f"{DIM}Using default: {chosen['label']}{RESET}")
return chosen
else:
print(f"{COLOR_ERROR}Default model unavailable.
Please select another.{RESET}")
continue
if raw.isdigit() and 1 <= int(raw) <= len(MODELS):
chosen = MODELS[int(raw) - 1]
if ensure_model_available(chosen["id"]):
return chosen
else:
print(f"{COLOR_ERROR}Skipping —
please choose another model.{RESET}")
continue # loop back to prompt again
print(f"{COLOR_ERROR}Please enter a number
between 1 and {len(MODELS)}.{RESET}")
the long road around, and inefficiently solving a problem.
thinkingprocess.
experience: