Text Classification
PEFT
lora
document-question-answering
structured-decisions
calibration
synthetic-evaluation
Instructions to use botp/Solomon with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use botp/Solomon with PEFT:
Task type is invalid.
- Notebooks
- Google Colab
- Kaggle
Download src/solomon/retrieval.py from botp/Solomon: direct link, hf CLI and curl.
- Browser
- Download file 9.16 kB
-
https://huggingface.co/botp/Solomon/resolve/main/src/solomon/retrieval.py
- Command line
-
hf download hf://botp/Solomon/src/solomon/retrieval.py
-
curl -L -o retrieval.py https://huggingface.co/botp/Solomon/resolve/main/src/solomon/retrieval.py
9.16 kB
| """Inference-only source candidates and explicitly labelled retrieval baselines.""" | |
| import re | |
| from solomon.evidence import digest,rank_candidates,validate_spans | |
| def candidates(text): | |
| """Source sentences retaining exact character ranges; no authoring labels.""" | |
| ends=[m.end() for m in re.finditer(r'(?<=[.!?])(?:[ \t]+|\n+)|\n\s*\n',text)]+[len(text)] | |
| result=[];start=0 | |
| for end in ends: | |
| if end>start and text[start:end].strip(): | |
| result.append({'id':f'text:{start}:{end}','kind':'text','start':start,'end':end, | |
| 'text':text[start:end],'source_sha256':digest(text)}) | |
| start=end | |
| return result | |
| def governing_context(text,spans=None): | |
| """Generic rule/retraction interpretation context, independent of question gold.""" | |
| spans=candidates(text) if spans is None else spans | |
| pattern=re.compile(r'general rule|both limbs|same (?:person|courier|member|officer)|neither limb|missing requirement|silence|withdrawn passage|dates and seniority|a withdrawn|outside these|bands are|order of the levels|no.*precedence|each passage|a grant in force|an exception to a rule',re.I) | |
| return [s for s in spans if pattern.search(s['text'])] | |
| def merge(text,spans): | |
| intervals=[] | |
| for span in sorted(spans,key=lambda s:(s['start'],s['end'])): | |
| a,b=span['start'],span['end'] | |
| if intervals and a<=intervals[-1][1]:intervals[-1]=(intervals[-1][0],max(b,intervals[-1][1])) | |
| else:intervals.append((a,b)) | |
| result=[{'id':f'text:{a}:{b}','kind':'text','start':a,'end':b,'text':text[a:b],'source_sha256':digest(text)} for a,b in intervals] | |
| return validate_spans(text,result) | |
| def render_subset(text,spans,*,include_context=True): | |
| selected=merge(text,list(spans)+(governing_context(text) if include_context else [])) | |
| return '\n\n'.join(s['text'] for s in selected),selected | |
| _WORD=re.compile(r'\w+') | |
| _SCALE=re.compile(r'\bbands?\b|\bscale\b|from lowest to highest|order of the levels',re.I) | |
| POOL_V2={'name':'idf-rare-rule-v2','idf_limit':24,'rare_df':16,'rules':True} | |
| def _words(s):return set(_WORD.findall(s.casefold())) | |
| def rule_candidates(text,spans=None): | |
| """Question-independent rule, exception, withdrawal-convention and scale passages.""" | |
| spans=candidates(text) if spans is None else spans | |
| rules={s['id'] for s in governing_context(text,spans)} | |
| return [s for s in spans if s['id'] in rules or _SCALE.search(s['text'])] | |
| def candidate_pool(question,spans,text=None,*,idf_limit=24,rare_df=16,rules=True,**_): | |
| """Bounded source-only pool: document-IDF top-k, every passage sharing a rare | |
| question term (typically the subject's name), plus question-independent rules. | |
| Candidate order is source order. candidate_score stays the plain lexical-overlap | |
| fraction used by the frozen relevance-head feature, so heads remain comparable. | |
| Gold is never an input. | |
| """ | |
| import math | |
| if idf_limit<1 or rare_df<0:raise ValueError('invalid candidate pool policy') | |
| ranked=rank_candidates(question,spans,len(spans)) if spans else [] | |
| words=[_words(c['text']) for c in ranked];q=_words(question);n=len(ranked) | |
| df={} | |
| for ws in words: | |
| for w in ws:df[w]=df.get(w,0)+1 | |
| idf=[sum(math.log((n+1)/(df[w]+.5)) for w in ws&q) for ws in words] | |
| order=sorted(range(n),key=lambda i:(-idf[i],ranked[i]['start'])) | |
| reason={} | |
| for i in order[:idf_limit]:reason.setdefault(ranked[i]['id'],'idf') | |
| rare={w for w in q if df.get(w,0)<=rare_df} | |
| for c,ws in zip(ranked,words): | |
| if ws&rare:reason.setdefault(c['id'],'rare_term') | |
| if rules: | |
| for c in rule_candidates(text or '',ranked):reason.setdefault(c['id'],'rule') | |
| return sorted(({**c,'pool_reason':reason[c['id']]} for c in ranked if c['id'] in reason),key=lambda c:c['start']) | |
| _WITHDRAW=re.compile(r'withdr[ae]w|withdrawn|take back|disregard|rescind|retract|should not be relied|there is substituted|is deleted|expressly delete',re.I) | |
| _REFERENCE=re.compile(r'(?:message of|wrote on|message dated) (\d{1,2} [A-Z][a-z]+)|minute (\d+)|[Ee]ntry (\d+) of [Ss]chedule (\d+)|clause ([\d.]+)|passage in my message') | |
| _EXCEPTION=re.compile(r'does not apply to|subject to the exception|is an exception|except (?:where|that|for)\b',re.I) | |
| _STOP={'this','that','these','those','with','under','which','what','does','file','correspondence','record','records','recorded', | |
| 'minutes','agreement','bundle','messages','message','stand','stands','taking','reading','whole','strength','position', | |
| 'open','given','have','been','applies','apply','entitled','allowed','liberty','free','from','there','their','they', | |
| 'decisions','here','schedules','schedule','papers','office','shown','show','shows','case','matters','terms','place','placed'} | |
| DEPENDENCIES_V1={'name':'withdrawal-exception-v1','withdrawals':True,'exceptions':True,'prune_withdrawn':False,'rare_df':16} | |
| def _topic(words,exclude): | |
| return {w for w in words if len(w)>=4 and w not in _STOP and w not in exclude and not w.isdigit()} | |
| def _withdrawn_block(text,passage): | |
| """Source range that a withdrawal refers to (dated message or numbered minute); None if not resolvable.""" | |
| m=_REFERENCE.search(passage) | |
| if not m:return None | |
| if m.group(1):head=re.search(r'Message \d+\. '+re.escape(m.group(1))+r'\.',text) | |
| elif m.group(2):head=re.search(r'(?:^|\n)'+m.group(2)+r'\. ',text) | |
| else:return None | |
| if not head:return None | |
| end=text.find('\n\n',head.end());return head.start(),(len(text) if end<0 else end) | |
| def dependency_expand(question,spans,selected_ids,text,*,withdrawals=True,exceptions=True,prune_withdrawn=False,rare_df=16,**_): | |
| """Complete a unit's selected evidence with its withdrawal/exception dependencies. | |
| Source text and the already-selected passages only; never gold. Units with no | |
| selection are unchanged, so no-positive-support decisions are preserved. | |
| - withdrawal: a passage with a withdrawal verb AND an explicit reference (message/minute/ | |
| entry/clause) that names the question's subject (a rare capitalised name, also present in a | |
| selected passage) and its most specific in-source topic word; | |
| - exception: an exception clause immediately following a selected passage; | |
| - prune_withdrawn (optional): drop selected passages inside the dated message / numbered | |
| minute a kept withdrawal refers to, when they share its subject and a topic word. | |
| Returns {'keep','added','pruned'} as candidate id lists in source order. | |
| """ | |
| selected=[s for s in spans if s['id'] in set(selected_ids)] | |
| if not selected:return {'keep':[],'added':[],'pruned':[]} | |
| words=[_words(s['text']) for s in spans];df={} | |
| for ws in words: | |
| for w in ws:df[w]=df.get(w,0)+1 | |
| # Subject: capitalised non-initial question tokens (names) that are rare in the source. | |
| names=re.findall(r'(?<!^)(?<![.?!] )\b[A-Z][\w&\'-]*',question.strip()) | |
| subject={w for n in names for w in _words(n) if len(w)>=3 and df.get(w,0)<=rare_df} | |
| q=_words(question);present=[w for w in _topic(q,subject) if df.get(w,0)] | |
| # The withdrawal must name the question's most specific in-source topic word (e.g. the attribute). | |
| topic={min(present,key=lambda w:(df[w],w))} if present else set() | |
| chosen={s['id'] for s in selected};sel_words=set().union(*(_words(s['text']) for s in selected));added=[] | |
| for i,(s,ws) in enumerate(zip(spans,words)): | |
| if s['id'] in chosen:continue | |
| if (withdrawals and _WITHDRAW.search(s['text']) and _REFERENCE.search(s['text']) | |
| and ws&subject&sel_words and ws&topic):added.append(s['id']);continue | |
| if exceptions and i>0 and spans[i-1]['id'] in chosen and _EXCEPTION.search(s['text']):added.append(s['id']) | |
| keep=chosen|set(added);pruned=[] | |
| if prune_withdrawn: | |
| for s,ws in zip(spans,words): | |
| if s['id'] not in keep or not _WITHDRAW.search(s['text']):continue | |
| block=_withdrawn_block(text,s['text']) | |
| if block is None:continue | |
| key=ws&subject;about=_topic(ws,subject) | |
| for t,tw in zip(spans,words): | |
| if (t['id'] in keep and t['id']!=s['id'] and block[0]<=t['start'] and t['end']<=block[1]+2 | |
| and tw&key and tw&about and not _WITHDRAW.search(t['text'])):pruned.append(t['id']) | |
| order=[s['id'] for s in spans];pruned=set(pruned) | |
| return {'keep':[i for i in order if i in keep and i not in pruned],'added':[i for i in order if i in set(added)], | |
| 'pruned':[i for i in order if i in pruned]} | |
| def lexical_select(text,questions,limit=4): | |
| source=candidates(text);chosen={} | |
| for q in questions: | |
| for candidate in rank_candidates(q,source,limit): | |
| if candidate['candidate_score']>0:chosen[candidate['id']]=candidate | |
| return {'evidence':sorted(chosen.values(),key=lambda c:c['start']), | |
| 'method':'lexical_overlap','verification':'retrieval_only','faithfulness_established':False} | |
| def remove(text,spans): | |
| spans=merge(text,spans);cursor=0;parts=[] | |
| for s in spans:parts.append(text[cursor:s['start']]);cursor=s['end'] | |
| parts.append(text[cursor:]);return ''.join(parts) | |