trooth 0.2.0__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {trooth-0.2.0 → trooth-0.3.0}/PKG-INFO +4 -1
  2. {trooth-0.2.0 → trooth-0.3.0}/README.md +3 -0
  3. {trooth-0.2.0 → trooth-0.3.0}/pyproject.toml +1 -1
  4. {trooth-0.2.0 → trooth-0.3.0}/trooth/__init__.py +1 -1
  5. {trooth-0.2.0 → trooth-0.3.0}/trooth.egg-info/PKG-INFO +4 -1
  6. {trooth-0.2.0 → trooth-0.3.0}/trooth_semanto_verify/__init__.py +1 -1
  7. {trooth-0.2.0 → trooth-0.3.0}/trooth_semanto_verify/answer.py +17 -0
  8. {trooth-0.2.0 → trooth-0.3.0}/trooth_semanto_verify/source.py +5 -1
  9. {trooth-0.2.0 → trooth-0.3.0}/LICENSE +0 -0
  10. {trooth-0.2.0 → trooth-0.3.0}/setup.cfg +0 -0
  11. {trooth-0.2.0 → trooth-0.3.0}/trooth/integrations/__init__.py +0 -0
  12. {trooth-0.2.0 → trooth-0.3.0}/trooth/integrations/langchain.py +0 -0
  13. {trooth-0.2.0 → trooth-0.3.0}/trooth/integrations/llama_index.py +0 -0
  14. {trooth-0.2.0 → trooth-0.3.0}/trooth/integrations/openai_agents.py +0 -0
  15. {trooth-0.2.0 → trooth-0.3.0}/trooth.egg-info/SOURCES.txt +0 -0
  16. {trooth-0.2.0 → trooth-0.3.0}/trooth.egg-info/dependency_links.txt +0 -0
  17. {trooth-0.2.0 → trooth-0.3.0}/trooth.egg-info/entry_points.txt +0 -0
  18. {trooth-0.2.0 → trooth-0.3.0}/trooth.egg-info/requires.txt +0 -0
  19. {trooth-0.2.0 → trooth-0.3.0}/trooth.egg-info/top_level.txt +0 -0
  20. {trooth-0.2.0 → trooth-0.3.0}/trooth_semanto_verify/__main__.py +0 -0
  21. {trooth-0.2.0 → trooth-0.3.0}/trooth_semanto_verify/canonical.py +0 -0
  22. {trooth-0.2.0 → trooth-0.3.0}/trooth_semanto_verify/cli.py +0 -0
  23. {trooth-0.2.0 → trooth-0.3.0}/trooth_semanto_verify/client.py +0 -0
  24. {trooth-0.2.0 → trooth-0.3.0}/trooth_semanto_verify/httpsig.py +0 -0
  25. {trooth-0.2.0 → trooth-0.3.0}/trooth_semanto_verify/jws.py +0 -0
  26. {trooth-0.2.0 → trooth-0.3.0}/trooth_semanto_verify/merkle.py +0 -0
  27. {trooth-0.2.0 → trooth-0.3.0}/trooth_semanto_verify/verify.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trooth
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Read a company's Semanto signed copy and check it: signatures, keys, DNS binding, page proofs and the public log.
5
5
  Author: Trooth, LLC
6
6
  License-Expression: Apache-2.0
@@ -94,6 +94,9 @@ from trooth import SemantoSource
94
94
  acme = SemantoSource("acme.com", on_event=lambda e: print(e["event"], e["detail"]))
95
95
  a = acme.answer(question="How much is the Starter plan?")
96
96
  print(a["status"], a["text"])
97
+
98
+ r = acme.reply(question=user_question, draft=model_text) # built by code; the draft is discarded
99
+ show(r["text"])
97
100
  ```
98
101
 
99
102
  It loads `/.well-known/trooth.semanto` and the company's keys from the company's site, checks the file once for each new body, reads the site again at most every 15 minutes, and answers fact IDs and plain questions in process with the same matcher as Trooth's service (`trooth_semanto_verify.answer`, compared case by case with `verifier/test-vectors/answer-v1`). When Trooth's API cannot be reached it keeps answering from the last version that checked out and logs `trooth_api_unreachable`; a new version that does not check out is refused and the last good one stays in use. A fact marked changed since signing or expired returns its mark and no value.
@@ -64,6 +64,9 @@ from trooth import SemantoSource
64
64
  acme = SemantoSource("acme.com", on_event=lambda e: print(e["event"], e["detail"]))
65
65
  a = acme.answer(question="How much is the Starter plan?")
66
66
  print(a["status"], a["text"])
67
+
68
+ r = acme.reply(question=user_question, draft=model_text) # built by code; the draft is discarded
69
+ show(r["text"])
67
70
  ```
68
71
 
69
72
  It loads `/.well-known/trooth.semanto` and the company's keys from the company's site, checks the file once for each new body, reads the site again at most every 15 minutes, and answers fact IDs and plain questions in process with the same matcher as Trooth's service (`trooth_semanto_verify.answer`, compared case by case with `verifier/test-vectors/answer-v1`). When Trooth's API cannot be reached it keeps answering from the last version that checked out and logs `trooth_api_unreachable`; a new version that does not check out is refused and the last good one stays in use. A fact marked changed since signing or expired returns its mark and no value.
@@ -12,7 +12,7 @@ build-backend = "setuptools.build_meta"
12
12
 
13
13
  [project]
14
14
  name = "trooth"
15
- version = "0.2.0"
15
+ version = "0.3.0"
16
16
  description = "Read a company's Semanto signed copy and check it: signatures, keys, DNS binding, page proofs and the public log."
17
17
  readme = "README.md"
18
18
  requires-python = ">=3.11"
@@ -14,5 +14,5 @@ kits). Framework integrations are optional extras: trooth[langchain], trooth[lla
14
14
  from trooth_semanto_verify.client import SemantoClient
15
15
  from trooth_semanto_verify.source import SemantoSource
16
16
 
17
- __version__ = "0.2.0"
17
+ __version__ = "0.3.0"
18
18
  __all__ = ["SemantoClient", "SemantoSource", "__version__"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trooth
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Read a company's Semanto signed copy and check it: signatures, keys, DNS binding, page proofs and the public log.
5
5
  Author: Trooth, LLC
6
6
  License-Expression: Apache-2.0
@@ -94,6 +94,9 @@ from trooth import SemantoSource
94
94
  acme = SemantoSource("acme.com", on_event=lambda e: print(e["event"], e["detail"]))
95
95
  a = acme.answer(question="How much is the Starter plan?")
96
96
  print(a["status"], a["text"])
97
+
98
+ r = acme.reply(question=user_question, draft=model_text) # built by code; the draft is discarded
99
+ show(r["text"])
97
100
  ```
98
101
 
99
102
  It loads `/.well-known/trooth.semanto` and the company's keys from the company's site, checks the file once for each new body, reads the site again at most every 15 minutes, and answers fact IDs and plain questions in process with the same matcher as Trooth's service (`trooth_semanto_verify.answer`, compared case by case with `verifier/test-vectors/answer-v1`). When Trooth's API cannot be reached it keeps answering from the last version that checked out and logs `trooth_api_unreachable`; a new version that does not check out is refused and the last good one stays in use. A fact marked changed since signing or expired returns its mark and no value.
@@ -91,4 +91,4 @@ __all__ = [
91
91
  "version_id",
92
92
  ]
93
93
 
94
- __version__ = "0.2.0"
94
+ __version__ = "0.3.0"
@@ -170,3 +170,20 @@ def answer(file: dict, company: dict, fact_id: Optional[str] = None, question: O
170
170
  lines = [MARK_WORDS[mark]] if mark else [f["statement"], *([f"Conditions: {'; '.join(f['conditions'])}."] if f.get("conditions") else [])]
171
171
  lines.append(f"Check: {check}")
172
172
  return {**base, "status": "answer", "matched_by": by, "facts": [_as_answered(f, mark)], "text": "\n".join(lines[:3])}
173
+
174
+
175
+ def assemble_reply(a: dict, draft: Optional[str] = None) -> dict:
176
+ """What the customer is shown, built by code from an answer; a model's own wording is never part of it.
177
+
178
+ For a value answer the first line is the signed sentence exactly as signed, then its conditions if any, then the day
179
+ it was signed and the check link. A marked fact shows its mark instead of a value. Candidates, the not-published
180
+ reply and not_read show the answer's own fixed line. `draft` (what a model wrote) is accepted and discarded:
181
+ `discarded` says whether one was given."""
182
+ discarded = isinstance(draft, str) and len(draft) > 0
183
+ signed_on = str(a["signed_at"])[:10] if a.get("signed_at") else None
184
+ f = a["facts"][0] if a["status"] == "answer" else None
185
+ if not f:
186
+ return {"status": a["status"], "fact_id": None, "statement": None, "signed_on": signed_on, "check": a.get("check"), "text": a["text"], "discarded": discarded}
187
+ tail = f"{f'Signed on {signed_on}. ' if signed_on else ''}Check: {a['check']}"
188
+ lines = [MARK_WORDS[f["mark"]], tail] if f["mark"] else [f["statement"], *([f"Conditions: {'; '.join(f['conditions'])}."] if f["conditions"] else []), tail]
189
+ return {"status": a["status"], "fact_id": f["fact_id"], "statement": f["statement"], "signed_on": signed_on, "check": a["check"], "text": "\n".join(lines), "discarded": discarded}
@@ -28,7 +28,7 @@ from datetime import datetime, timezone
28
28
  from typing import Any, Callable, Dict, List, Optional
29
29
 
30
30
  from .answer import answer as _answer
31
- from .answer import fact_marks
31
+ from .answer import assemble_reply, fact_marks
32
32
  from .client import Fetch, urllib_fetch
33
33
  from .verify import verify_semanto
34
34
 
@@ -152,3 +152,7 @@ class SemantoSource:
152
152
  return {"object": "semanto.answer", "status": "not_read", "matched_by": None, "company": {"trooth_id": None, "name": None, "domain": self.domain},
153
153
  "version": None, "facts": [], "text": f"No signed record of {self.domain} could be read and checked yet."}
154
154
  return _answer(self.file, self.company, fact_id=fact_id, question=question, marks=self.marks(), check_base=f"{self.api_base}/public/semanto")
155
+
156
+ def reply(self, fact_id: Optional[str] = None, question: Optional[str] = None, draft: Optional[str] = None) -> dict:
157
+ """The reply a chatbot shows for a question: built by code from the signed record, with the model's draft discarded."""
158
+ return assemble_reply(self.answer(fact_id=fact_id, question=question), draft)
File without changes
File without changes