dopetalk does not endorse any advertised product nor does it accept any liability for it's use or misuse


Our Discord Notification Server invitation link is https://discord.gg/jB2qmRrxyD

Author Topic: AI Preprocessor  (Read 10 times)

Offline Chip (OP)

  • Server Admin
  • Hero Member
  • *****
  • Administrator
  • *****
  • Join Date: Dec 2014
  • Location: Australia
  • Posts: 7257
  • Reputation Power: 0
  • Chip has hidden their reputation power
  • Gender: Male
  • Last Login:Today at 08:59:38 AM
  • Deeply Confused Learner
  • Profession: IT Engineer now retired
AI Preprocessor
« on: Today at 08:18:32 AM »
This was our testing command, data and the optimised result, ready for input to AI:

Code: [Select]
echo "Um, err, can you shed some light on what spaCy can really do when one- kind- of gets the hang of it ?" | python3 ai-preproc.py -

Code: [Select]
Shed light on what spaCy can really do when one kind of gets the hang of it

Fails when parsed a lot of data:

Code: [Select]
python3 ai-preproc.py transcript.txt

transcript.txt

Code: [Select]
AG is basically mandatory, not optional — anywhere requiring private, proprietary, or fast- changing data the model was never trained on: internal company wikis, live inventory/pricing, legal case law, customer support histories. Standalone LLMs have no access to any of that no matter how big their window is — that data doesn't exist in their weights, full stop.

Where it's skippable, and often is — general reasoning, coding help, writing assistance, ideation, or anything where the model's frozen training knowledge is already sufficient — adding retrieval here mostly adds latency and complexity without meaningfully improving the answer.

So the honest answer: RAG is dominant in enterprise/knowledge- work AI products specifically, genuinely optional or actively being replaced in some 2026 pipelines as context windows grow, and simply absent in the plain chatbot use case you and I have been running most of this conversation in.[/color]

[color=purple]Me: but if you use RAG to access another of my conversations, would this be considered RAG ?[/color]

[color=navy]AI: That's actually the exact case I described earlier under "using another LLM" — worth distinguishing it clearly from RAG's more common use, since the term gets applied to two genuinely different jobs.

RAG for external knowledge (the common case) — retrieval brings in facts the model was never trained on: your company's internal docs, live data, things outside the weights entirely. The point is adding knowledge that doesn't exist in the model.

RAG for context management (what you're asking about) — here the information isn't new — it's stuff that was already said earlier in the same conversation or session, but has scrolled outside the current context window. Retrieval's job isn't "give the model new facts," it's "give the model back something it technically already knows but can no longer see." Same mechanism (embed, store, retrieve by similarity, reinsert), completely different purpose.

This is exactly the "long- term store" and "hierarchical memory" pattern from earlier: old conversation turns get embedded and stored once they age out of the live window, and when something later in the conversation seems related, the retrieval step pulls the relevant fragment back in — not from an external knowledge base, but from the conversation's own history.

The genuinely useful distinction: when RAG is used for external knowledge, a "miss" means the model doesn't know a fact it was never told. When RAG is used for context management, a "miss" is worse in one sense — the information was said, the user reasonably expects the system to remember it, and a failed retrieval looks like forgetting rather than not- knowing. That's part of why "lost in the middle" and retrieval quality matter even more here than in the standard document- QA RAG case — the user's trust that the system is actually listening is directly on the line.

This is also functionally close to what my own memory system is doing with you across sessions, just at a much coarser grain — periodic summarization instead of fine- grained per- turn retrieval. Same underlying idea: don't force everything into the live window, selectively bring back what's relevant when it's needed.[/color]

[color=purple]Me: what is lost in the middle[/color]

[color=navy]AI: You actually asked about this a bit earlier in this conversation, so briefly, in case it didn't fully land the first time, then I'll go deeper if useful.

The core phenomenon: LLMs don't attend evenly across a long context. When relevant information sits at the very start or very end of a long prompt, models retrieve and use it reliably. When that same information sits buried in the middle, accuracy drops - sometimes sharply - even though the information is technically right there, fully within the context window, and the model has more than enough capacity to hold it.

Why it happens, mechanically: it traces back to training data patterns and how attention gets distributed. Models tend to develop a bias toward the start of a sequence (recency- from- the- beginning) and the end (recency- from- now), because those positions are disproportionately informative during training - the beginning often sets up the task, the end is what's freshest. The middle doesn't get that same "special treatment" baked in, so attention weights end up systematically weaker there, independent of the content's actual relevance.



Here is the final build of the ai-preproc.py that merges Claude's original preprocessor with Copilot's NLP preprocessor that Claude debugged

Code: [Select]
#!/usr/bin/env python3
"""
ai-preproc.py
Merged two-stage pipeline: token_preprocessor.py + canonical_rewriter.py
combined into one script, one command.

STAGE 1 (cleanup, from token_preprocessor.py):
    - Fix double-encoded UTF-8 (mojibake, e.g. "a€"" -> "-")
    - Strip BBCode tags ([b], [color=...], etc.)
    - Fix broken-hyphenation PDF-extraction artifacts
    - Collapse page-break markers
    - Shorten verbose phrases ("utilize" -> "use", etc.)
    - Collapse excess whitespace

STAGE 2 (canonicalization, from canonical_rewriter.py):
    - Strip conversational/voice filler ("um", "please", "can you", etc.)
    - Split into sentences
    - Extract each sentence's core verb + object (walking ROOT, ccomp,
      prep, pcomp, and advmod-mediated attachments - see inline notes,
      each confirmed against real spaCy parses via debug_parse.py)
    - Rebuild each sentence as a short canonical command phrase

Usage:
    python3 ai-preproc.py input.txt output.txt
    python3 ai-preproc.py input.txt            # prints to stdout
    cat input.txt | python3 ai-preproc.py -  # read from stdin
    python3 ai-preproc.py                         # runs built-in samples
"""

import re
import sys
import spacy

nlp = spacy.load("en_core_web_sm")


# =======================================================================
# STAGE 1 - Cleanup (from token_preprocessor.py)
# =======================================================================

def fix_mojibake(text: str) -> str:
    """Repair double-encoded UTF-8 (e.g. em-dashes turned into a€" )."""
    try:
        return text.encode('cp1252').decode('utf-8')
    except (UnicodeEncodeError, UnicodeDecodeError):
        return text


BBCODE_TAG_RE = re.compile(r'\[/?(?:color|b|i|u|size|url|quote|img)[^\]]*\]', re.IGNORECASE)

def strip_bbcode(text: str) -> str:
    return BBCODE_TAG_RE.sub('', text)


BROKEN_HYPHEN_RE = re.compile(r'(\w)(?:\s+-\s*|\s*-\s+)(\w)')

def fix_broken_hyphenation(text: str) -> str:
    prev = None
    while prev != text:
        prev = text
        text = BROKEN_HYPHEN_RE.sub(r'\1 \2', text)
    return text


PAGE_MARKER_RE = re.compile(r'=+\s*Page\s*\d+\s*=+', re.IGNORECASE)

def strip_page_markers(text: str) -> str:
    return PAGE_MARKER_RE.sub('', text)


PHRASE_MAP = [
    ("in order to", "to"),
    ("due to the fact that", "because"),
    ("in the event that", "if"),
    ("with regard to", "about"),
    ("with respect to", "about"),
    ("in spite of the fact that", "although"),
    ("for the purpose of", "to"),
    ("at this point in time", "now"),
    ("in the process of", "while"),
    ("a large number of", "many"),
    ("the majority of", "most"),
    ("utilize", "use"),
    ("utilization", "use"),
    ("commence", "start"),
    ("terminate", "end"),
    ("facilitate", "help"),
    ("subsequently", "then"),
    ("approximately", "about"),
    ("in conjunction with", "with"),
    ("prior to", "before"),
    ("subsequent to", "after"),
]

def shorten_phrases(text: str) -> str:
    for verbose, plain in PHRASE_MAP:
        pattern = re.compile(r'\b' + re.escape(verbose) + r'\b', re.IGNORECASE)
        text = pattern.sub(plain, text)
    return text


def collapse_whitespace(text: str) -> str:
    text = re.sub(r'[ \t]+', ' ', text)
    text = re.sub(r'\n{3,}', '\n\n', text)
    text = re.sub(r' +\n', '\n', text)
    return text.strip()


def stage1_cleanup(text: str) -> str:
    text = fix_mojibake(text)
    text = strip_bbcode(text)
    text = strip_page_markers(text)
    text = fix_broken_hyphenation(text)
    text = shorten_phrases(text)
    text = collapse_whitespace(text)
    return text


# =======================================================================
# STAGE 2 - Canonicalization (from canonical_rewriter.py)
# =======================================================================

VERB_MAP = {
    "clarify": "explain",
    "describe": "explain",
    "tell": "explain",
    "show": "demonstrate",
    "give": "provide",
    "list": "list",
    "identify": "identify",
    "explain": "explain",
    "define": "define",
    "summarize": "summarize",
    "compare": "compare",
    "analyze": "analyze",
}

FILLER_PHRASES = [
    "before we proceed",
    "at this point",
    "if possible",
    "could you",
    "can you",
    "would you",
    "please",
    "kindly",
    "i was wondering",
    "i want to know",
    "i need to know",
    "um,", "um",
    "uh,", "uh",
    "err,", "err",
    "erm,", "erm",
    "you know,", "you know",
    "i mean,", "i mean",
    # NOTE: "sort of" and "kind of" deliberately removed from this list.
    # They're too ambiguous for blind string matching - "what KIND OF
    # things spaCy can do" is genuine content (a noun phrase), not a
    # hedge, but "it's kind of hard" is filler. A context-free string
    # match can't tell these apart, and deleting real content is worse
    # than missing an occasional hedge. Confirmed via live testing:
    # this exact collision silently ate "kind of" out of a real sentence.
]


def remove_filler(text):
    cleaned = text
    for phrase in FILLER_PHRASES:
        pattern = re.compile(re.escape(phrase), re.IGNORECASE)
        cleaned = pattern.sub("", cleaned)
    cleaned = re.sub(r"\s{2,}", " ", cleaned)
    cleaned = re.sub(r"^[,\s]+", "", cleaned)
    return cleaned.strip()


def extract_main_verb(doc):
    for token in doc:
        if token.pos_ == "VERB" and token.dep_ == "ROOT":
            return token
    for token in doc:
        if token.pos_ == "VERB" and token.dep_ in ("ccomp", "advcl"):
            return token
    return None


def _expand_with_trailing_prep_phrase(token):
    for child in token.children:
        if child.dep_ == "prep":
            for grandchild in child.children:
                if grandchild.dep_ == "pobj":
                    start = token.left_edge.i
                    end = grandchild.right_edge.i
                    return token.doc[start:end + 1].text
    return token.text


def _verb_level_prep_phrase(verb):
    def _phrase_from_prep(prep_token):
        for grandchild in prep_token.children:
            if grandchild.dep_ in ("pobj", "pcomp"):
                start = prep_token.left_edge.i
                end = grandchild.right_edge.i
                return prep_token.doc[start:end + 1].text
        return None

    for child in verb.children:
        if child.dep_ == "prep":
            phrase = _phrase_from_prep(child)
            if phrase:
                return phrase

    for child in verb.children:
        if child.dep_ == "advmod":
            for grandchild in child.children:
                if grandchild.dep_ == "prep":
                    phrase = _phrase_from_prep(grandchild)
                    if phrase:
                        return phrase

    return None


def extract_object(verb):
    direct = [child for child in verb.children if child.dep_ in ("dobj", "attr", "oprd")]
    obj_text = _expand_with_trailing_prep_phrase(direct[0]) if direct else None

    verb_prep_text = _verb_level_prep_phrase(verb)

    if obj_text and verb_prep_text:
        if verb_prep_text not in obj_text:
            return f"{obj_text} {verb_prep_text}"
        return obj_text
    if obj_text:
        return obj_text
    if verb_prep_text:
        return verb_prep_text

    for child in verb.children:
        if child.dep_ == "ccomp":
            nested_obj = extract_object(child)
            if nested_obj:
                return nested_obj
            nested_prep = _verb_level_prep_phrase(child)
            if nested_prep:
                return nested_prep

    return None


def extract_entities(doc):
    return [ent.text for ent in doc.ents]


def canonical_rewrite_sentence(sent):
    verb = extract_main_verb(sent)
    if not verb:
        return None

    canonical_verb = VERB_MAP.get(verb.lemma_, verb.lemma_)
    obj = extract_object(verb)
    entities = extract_entities(sent)

    parts = [canonical_verb.capitalize()]
    if obj:
        parts.append(obj)
    if entities:
        new_entities = [e for e in entities if not (obj and e in obj)]
        if new_entities:
            parts.append("about " + ", ".join(new_entities))

    return " ".join(parts)


def stage2_canonicalize(text: str) -> str:
    cleaned = remove_filler(text)
    doc = nlp(cleaned)

    results = []
    for sent in doc.sents:
        rewritten = canonical_rewrite_sentence(sent)
        if rewritten:
            results.append(rewritten)

    if not results:
        return cleaned

    return " | ".join(results)


# =======================================================================
# Combined pipeline
# =======================================================================

def run_pipeline(text: str) -> str:
    stage1 = stage1_cleanup(text)
    stage2 = stage2_canonicalize(stage1)
    return stage2


if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(__doc__)
        print("With no arguments, runs the built-in sample sentences instead.\n")
        samples = [
            "Before we proceed, can you clarify what you meant earlier about memory?",
            "Can you describe the process for token generation?",
            "Please give me a summary of the KV cache splicing problem.",
            "Um, err, can you shed some light on what spaCy can really do when one gets the hang of it?",
        ]
        for sample in samples:
            print("Original: ", sample)
            print("Canonical:", run_pipeline(sample))
            print()
        sys.exit(0)

    src = sys.argv[1]
    if src == "-":
        raw = sys.stdin.read()
    else:
        with open(src, "r", encoding="utf-8") as f:
            raw = f.read()

    output_text = run_pipeline(raw)

    if len(sys.argv) >= 3:
        with open(sys.argv[2], "w", encoding="utf-8") as f:
            f.write(output_text)
        print(f"Written to {sys.argv[2]}")
    else:
        print(output_text)


Here is the final build of canonical_rewriter.py whicj is Copilot's preprocessor that Claude debugged and fixed -


Code: [Select]
#!/usr/bin/env python3
"""
canonical_rewriter.py
General-purpose canonical query rewriter.
No per-query editing. No hard-coded samples.

Fixes applied to Copilot's version, and why:

1. remove_filler() used to lowercase the ENTIRE text before parsing.
   spaCy's model relies on capitalization (proper nouns, acronyms,
   sentence boundaries) to parse correctly - feeding it all-lowercase
   text makes acronym/entity detection worse, not better.
   Fix: strip filler phrases case-insensitively, but preserve the
   original casing in the text that actually gets parsed.

2. Verb selection took the FIRST verb matching ROOT/ccomp/advcl in
   linear order, so a subordinate clause verb appearing earlier in
   the sentence could be picked over the real ROOT verb.
   Fix: search for ROOT explicitly first, fall back to ccomp/advcl
   only if no ROOT verb exists.

3. Object extraction only checked the verb's DIRECT children for
   dobj/pobj/attr/oprd, missing objects nested inside prepositional
   phrases (verb -> prep -> pobj, e.g. "clarify ... about memory"),
   and missing further nested phrases attached to the object itself
   (object -> prep -> pobj, e.g. "summary OF the KV cache problem").
   Fix: walk both levels and return the full phrase span, not just
   the bare head noun.

4. Entities that are already contained within the extracted object
   phrase used to get appended a second time (e.g. "KV" appearing
   both inside the object phrase and again as "about KV" from NER
   tagging the acronym separately).
   Fix: only append entities not already covered by the object text.
"""

import re
import spacy

nlp = spacy.load("en_core_web_sm")

# Universal polite -> command verb mapping
VERB_MAP = {
    "clarify": "explain",
    "describe": "explain",
    "tell": "explain",
    "show": "demonstrate",
    "give": "provide",
    "list": "list",
    "identify": "identify",
    "explain": "explain",
    "define": "define",
    "summarize": "summarize",
    "compare": "compare",
    "analyze": "analyze",
}

# Universal filler removal
FILLER_PHRASES = [
    "before we proceed",
    "at this point",
    "if possible",
    "could you",
    "can you",
    "would you",
    "please",
    "kindly",
    "i was wondering",
    "i want to know",
    "i need to know",
    # Voice-to-text verbal fillers (added for voice input support)
    "um,",
    "um",
    "uh,",
    "uh",
    "err,",
    "err",
    "erm,",
    "erm",
    "you know,",
    "you know",
    "i mean,",
    "i mean",
    # NOTE: "sort of" and "kind of" deliberately removed - blind string
    # match can't distinguish genuine content ("what KIND OF things...")
    # from hedging filler ("it's kind of hard"). Confirmed via live
    # testing that this collision silently deletes real content.
]


def remove_filler(text):
    # Case-insensitive matching, but preserve original casing in the
    # text that gets returned/parsed - spaCy needs real capitalization
    # to parse acronyms and proper nouns correctly.
    cleaned = text
    for phrase in FILLER_PHRASES:
        pattern = re.compile(re.escape(phrase), re.IGNORECASE)
        cleaned = pattern.sub("", cleaned)
    cleaned = re.sub(r"\s{2,}", " ", cleaned)
    cleaned = re.sub(r"^[,\s]+", "", cleaned)
    return cleaned.strip()


def extract_main_verb(doc):
    for token in doc:
        if token.pos_ == "VERB" and token.dep_ == "ROOT":
            return token
    for token in doc:
        if token.pos_ == "VERB" and token.dep_ in ("ccomp", "advcl"):
            return token
    return None


def _expand_with_trailing_prep_phrase(token):
    for child in token.children:
        if child.dep_ == "prep":
            for grandchild in child.children:
                if grandchild.dep_ == "pobj":
                    start = token.left_edge.i
                    end = grandchild.right_edge.i
                    return token.doc[start:end + 1].text
    return token.text


def _verb_level_prep_phrase(verb):
    """
    Find a prepositional phrase associated with the verb. Checks two
    attachment points, and two kinds of preposition complement:

    Attachment points:
      1. Direct child of the verb - e.g. "describe X FOR token generation".
      2. Child of an adverb (advmod) modifying the verb - e.g.
         "meant EARLIER ABOUT memory" (confirmed via debug_parse.py).

    Complement types:
      - pobj: a simple noun object ("about MEMORY").
      - pcomp: a whole embedded clause ("on WHAT SPACY CAN REALLY DO
        WHEN ONE GETS THE HANG OF IT") - confirmed via debug_parse.py
        on the "shed light on..." sentence. When this happens we grab
        the full span through the end of that clause's subtree, since
        the "object" here isn't a single word.
    """
    def _phrase_from_prep(prep_token):
        for grandchild in prep_token.children:
            if grandchild.dep_ == "pobj":
                start = prep_token.left_edge.i
                end = grandchild.right_edge.i
                return prep_token.doc[start:end + 1].text
            if grandchild.dep_ == "pcomp":
                start = prep_token.left_edge.i
                end = grandchild.right_edge.i
                return prep_token.doc[start:end + 1].text
        return None

    # Case 1: prep directly on the verb.
    for child in verb.children:
        if child.dep_ == "prep":
            phrase = _phrase_from_prep(child)
            if phrase:
                return phrase

    # Case 2: prep nested under an adverb modifying the verb.
    for child in verb.children:
        if child.dep_ == "advmod":
            for grandchild in child.children:
                if grandchild.dep_ == "prep":
                    phrase = _phrase_from_prep(grandchild)
                    if phrase:
                        return phrase

    return None


def extract_object(verb):
    direct = [child for child in verb.children if child.dep_ in ("dobj", "attr", "oprd")]
    obj_text = _expand_with_trailing_prep_phrase(direct[0]) if direct else None

    verb_prep_text = _verb_level_prep_phrase(verb)

    # A direct object and a verb-level prep phrase can coexist
    # ("process" + "for token generation") - combine rather than
    # treating them as either/or.
    if obj_text and verb_prep_text:
        if verb_prep_text not in obj_text:
            return f"{obj_text} {verb_prep_text}"
        return obj_text
    if obj_text:
        return obj_text
    if verb_prep_text:
        return verb_prep_text

    # Nothing found on the verb itself - check if the verb has a ccomp
    # (a nested complement clause, e.g. "clarify [what you meant about
    # memory]") and recurse into THAT clause's own verb to find its
    # object. This is what "clarify what you meant... about memory"
    # needs: the real content is one level deeper than "clarify" itself.
    for child in verb.children:
        if child.dep_ == "ccomp":
            nested_obj = extract_object(child)
            if nested_obj:
                return nested_obj
            nested_prep = _verb_level_prep_phrase(child)
            if nested_prep:
                return nested_prep

    return None


def extract_entities(doc):
    return [ent.text for ent in doc.ents]


def canonical_rewrite_sentence(sent):
    """
    Runs the verb/object/entity extraction on a single sentence span
    (not a whole paragraph). Returns the canonical phrase for just
    that sentence, or None if no usable verb was found.
    """
    verb = extract_main_verb(sent)
    if not verb:
        return None

    canonical_verb = VERB_MAP.get(verb.lemma_, verb.lemma_)
    obj = extract_object(verb)
    entities = extract_entities(sent)  # entities within this sentence only

    parts = [canonical_verb.capitalize()]

    if obj:
        parts.append(obj)

    if entities:
        new_entities = [e for e in entities if not (obj and e in obj)]
        if new_entities:
            parts.append("about " + ", ".join(new_entities))

    return " ".join(parts)


def canonical_rewrite(text: str) -> str:
    """
    Processes a whole paragraph: splits into sentences (spaCy's own
    sentence boundary detection via doc.sents), canonicalizes each
    sentence separately, then joins the results. This is what makes
    it paragraph-safe rather than only picking up the first verb in
    the entire blob and silently dropping every sentence after it.
    """
    cleaned = remove_filler(text)
    doc = nlp(cleaned)

    results = []
    for sent in doc.sents:
        rewritten = canonical_rewrite_sentence(sent)
        if rewritten:
            results.append(rewritten)

    if not results:
        return cleaned  # fallback: nothing extracted at all

    return " | ".join(results)


if __name__ == "__main__":
    import sys

    if len(sys.argv) < 2:
        print(__doc__)
        print("Usage: python3 canonical_rewriter.py input.txt [output.txt]")
        print("       python3 canonical_rewriter.py -   (read stdin)")
        print()
        print("With no arguments, runs the built-in sample sentences instead.")
        samples = [
            "Before we proceed, can you clarify what you meant earlier about memory?",
            "Can you describe the process for token generation?",
            "Please give me a summary of the KV cache splicing problem.",
        ]
        for sample in samples:
            print("Original: ", sample)
            print("Canonical:", canonical_rewrite(sample))
            print()
        sys.exit(0)

    src = sys.argv[1]
    if src == "-":
        raw = sys.stdin.read()
    else:
        with open(src, "r", encoding="utf-8") as f:
            raw = f.read()

    # Treat the whole file as ONE paragraph - canonical_rewrite()
    # handles splitting it into sentences internally now, so we don't
    # split by line here (a forum paragraph isn't one query per line).
    output_text = canonical_rewrite(raw)

    if len(sys.argv) >= 3:
        with open(sys.argv[2], "w", encoding="utf-8") as f:
            f.write(output_text)
        print(f"Written to {sys.argv[2]}")
    else:
        print(output_text)


This is debug_parse.py - Claude's debug tool


Code: [Select]
#!/usr/bin/env python3
"""
debug_parse.py
Prints the real dependency tree for a sentence: token, POS, dep label,
and head - so we can see exactly what's attached to what instead of
guessing.
"""
import spacy

nlp = spacy.load("en_core_web_sm")

sample = "ahh ... shed some light on what kind-of-things spaCy can really do when one gets the hang of it ?"

doc = nlp(sample)
print("SENTENCES spaCy detected:")
for i, sent in enumerate(doc.sents):
    print(f"  [{i}] {sent.text!r}")
print()
print(f"{'TOKEN':<12}{'POS':<8}{'DEP':<10}{'HEAD'}")
for token in doc:
    print(f"{token.text:<12}{token.pos_:<8}{token.dep_:<10}{token.head.text}")
« Last Edit: Today at 09:26:00 AM by smfadmin »
friendly
0
funny
0
informative
0
agree
0
disagree
0
like
0
dislike
0
No reactions
No reactions
No reactions
No reactions
No reactions
No reactions
No reactions
Our Discord Server invitation link is https://discord.gg/jB2qmRrxyD

Tags:
 


dopetalk does not endorse any advertised product nor does it accept any liability for it's use or misuse





TERMS AND CONDITIONS

In no event will d&u or any person involved in creating, producing, or distributing site information be liable for any direct, indirect, incidental, punitive, special or consequential damages arising out of the use of or inability to use d&u. You agree to indemnify and hold harmless d&u, its domain founders, sponsors, maintainers, server administrators, volunteers and contributors from and against all liability, claims, damages, costs and expenses, including legal fees, that arise directly or indirectly from the use of any part of the d&u site.


TO USE THIS WEBSITE YOU MUST AGREE TO THE TERMS AND CONDITIONS ABOVE


Founded December 2014
SimplePortal 2.3.6 © 2008-2014, SimplePortal