axel-protocol 2.3.4__tar.gz → 2.3.6__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 (38) hide show
  1. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/PKG-INFO +1 -1
  2. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/axel/cli.py +51 -18
  3. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/axel/server.py +3 -1
  4. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/axel/static/monitor.html +5 -3
  5. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/pyproject.toml +1 -1
  6. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/.github/workflows/ci.yml +0 -0
  7. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/.github/workflows/publish.yml +0 -0
  8. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/.gitignore +0 -0
  9. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/CHANGELOG.md +0 -0
  10. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/CONTRIBUTING.md +0 -0
  11. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/Dockerfile +0 -0
  12. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/LICENSE +0 -0
  13. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/README.md +0 -0
  14. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/axel/__init__.py +0 -0
  15. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/axel/adapters/__init__.py +0 -0
  16. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/axel/adapters/bedrock.py +0 -0
  17. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/axel/adapters/cohere.py +0 -0
  18. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/axel/adapters/gemini.py +0 -0
  19. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/axel/adapters/groq.py +0 -0
  20. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/axel/adapters/litellm.py +0 -0
  21. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/axel/adapters/mistral.py +0 -0
  22. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/axel/adapters/together.py +0 -0
  23. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/axel/client.py +0 -0
  24. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/axel/core.py +0 -0
  25. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/axel/learning.py +0 -0
  26. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/axel/persistence.py +0 -0
  27. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/axel/testing.py +0 -0
  28. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/axel/universal.py +0 -0
  29. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/docker-compose.yml +0 -0
  30. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/docs/message-schemas.md +0 -0
  31. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/examples/demo_live.py +0 -0
  32. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/install.sh +0 -0
  33. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/tests/__init__.py +0 -0
  34. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/tests/test_adapters.py +0 -0
  35. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/tests/test_client.py +0 -0
  36. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/tests/test_core.py +0 -0
  37. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/tests/test_new_features.py +0 -0
  38. {axel_protocol-2.3.4 → axel_protocol-2.3.6}/tests/test_server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: axel-protocol
3
- Version: 2.3.4
3
+ Version: 2.3.6
4
4
  Summary: AXEL — Agent eXchange Language: a universal protocol for multi-LLM networks
5
5
  Project-URL: Homepage, https://github.com/sectorx/axel-protocol
6
6
  Project-URL: Repository, https://github.com/sectorx/axel-protocol
@@ -488,14 +488,26 @@ _FREE_MODELS = [
488
488
  _SPECIALIST_MODELS = {
489
489
  "planner": "anthropic/claude-3-5-haiku", # fast, great at structured breakdown
490
490
  "researcher": "openai/gpt-4o-mini", # strong retrieval + summarisation
491
- "analyst": "google/gemini-2.0-flash-001", # quick pattern synthesis (updated ID)
492
- "fact-checker": "mistralai/mistral-large-2411", # rigorous verification (pinned version)
491
+ "analyst": "google/gemini-2.0-flash-001", # quick pattern synthesis
492
+ "fact-checker": "mistralai/mistral-large-2411", # rigorous verification
493
493
  "writer": "anthropic/claude-3-5-sonnet", # best prose quality
494
494
  "devil-advocate": "meta-llama/llama-3.3-70b-instruct", # challenges assumptions
495
495
  "summarizer": "qwen/qwen-2.5-72b-instruct", # distills to essence
496
496
  "reviewer": "openai/gpt-4o", # sharpest final critic
497
497
  }
498
498
 
499
+ # If a specialist model 404s or rate-limits, fall back to these automatically.
500
+ _FALLBACK_MODELS = {
501
+ "analyst": "google/gemini-flash-1.5-8b",
502
+ "fact-checker": "mistralai/mistral-small-3.1-24b-instruct",
503
+ "devil-advocate": "meta-llama/llama-3.1-70b-instruct",
504
+ "summarizer": "openai/gpt-4o-mini",
505
+ "reviewer": "openai/gpt-4o-mini",
506
+ "planner": "openai/gpt-4o-mini",
507
+ "researcher": "anthropic/claude-3-5-haiku",
508
+ "writer": "openai/gpt-4o-mini",
509
+ }
510
+
499
511
 
500
512
  def _openrouter(key: str, model: str, prompt: str, timeout: int = 45) -> str:
501
513
  """Call OpenRouter chat completion directly. Returns response text."""
@@ -744,20 +756,41 @@ def cmd_demo(args): # noqa: C901
744
756
  text = _MOCK_RESPONSES.get(action, f"[mock response for {action}]")
745
757
  print(f" [mock] {text[:180]}{'…' if len(text) > 180 else ''}")
746
758
  elif multi_model or single_model:
747
- # Use the specific model assigned to this agent
748
- try:
749
- text = _openrouter(key, mdl, full_prompt)
750
- print(f" [{to}/{mdl_short}] {text[:180]}{'…' if len(text) > 180 else ''}")
751
- except Exception as exc:
752
- print(f" ❌ {to} ({mdl_short}) failed: {exc}")
753
- return None
759
+ # Try specialist model, then fallback, then gpt-4o-mini as last resort
760
+ text = None
761
+ models_to_try = [mdl]
762
+ fallback = _FALLBACK_MODELS.get(to)
763
+ if fallback and fallback != mdl:
764
+ models_to_try.append(fallback)
765
+ models_to_try.append("openai/gpt-4o-mini") # always-available safety net
766
+ import urllib.error as _ue
767
+ for attempt_mdl in models_to_try:
768
+ attempt_short = attempt_mdl.split("/")[-1]
769
+ try:
770
+ text = _openrouter(key, attempt_mdl, full_prompt)
771
+ if attempt_mdl != mdl:
772
+ print(f" ⚠ Fell back to {attempt_short}")
773
+ print(f" [{to}/{attempt_short}] {text[:180]}{'…' if len(text) > 180 else ''}")
774
+ break
775
+ except _ue.HTTPError as e:
776
+ if e.code in (404, 429, 503):
777
+ print(f" ↩ {attempt_short} unavailable ({e.code}), trying next…")
778
+ continue
779
+ print(f" ❌ {to} ({attempt_short}) HTTP {e.code}")
780
+ break
781
+ except Exception as exc:
782
+ print(f" ❌ {to} ({attempt_short}): {exc}")
783
+ break
784
+ if text is None:
785
+ text = f"[{to} skipped — all models unavailable]"
786
+ print(f" ⚠ {to} skipped this step, pipeline continues…")
754
787
  else:
755
788
  try:
756
789
  _, text = _openrouter_free(key, full_prompt, label=to)
757
790
  except Exception as exc:
758
791
  print(f" ❌ {to} failed: {exc}")
759
792
  print(" Tip: run axel demo --mock to test without an API key")
760
- return None
793
+ text = f"[{to} skipped]"
761
794
  _fire_result(server, to, fr, action, {"text": text[:300]})
762
795
  if learn_key and learn_insight:
763
796
  try:
@@ -805,7 +838,7 @@ def cmd_demo(args): # noqa: C901
805
838
  learn_insight="Breaking missions into 3 focused subtasks improves agent handoff quality",
806
839
  mem_ctx=mem_ctx,
807
840
  )
808
- if not plan: break
841
+ if plan is None: continue # catastrophic failure — skip run
809
842
 
810
843
  # ── Step 2: researcher ────────────────────────────────
811
844
  print(f"\n STEP 2/{TOTAL} Researcher investigates each subtask")
@@ -816,7 +849,7 @@ def cmd_demo(args): # noqa: C901
816
849
  learn_insight="Structured subtask research produces more complete findings",
817
850
  mem_ctx=mem_ctx,
818
851
  )
819
- if not research: break
852
+ if research is None: continue
820
853
 
821
854
  # ── Step 3: analyst ───────────────────────────────────
822
855
  print(f"\n STEP 3/{TOTAL} Analyst synthesises the findings")
@@ -827,7 +860,7 @@ def cmd_demo(args): # noqa: C901
827
860
  learn_insight="Distilling findings to 3 insights makes downstream writing stronger",
828
861
  mem_ctx=mem_ctx,
829
862
  )
830
- if not analysis: break
863
+ if analysis is None: continue
831
864
 
832
865
  # ── Step 4: fact-checker ──────────────────────────────
833
866
  print(f"\n STEP 4/{TOTAL} Fact-Checker verifies the analysis")
@@ -839,7 +872,7 @@ def cmd_demo(args): # noqa: C901
839
872
  learn_insight="Flagging unverifiable claims before writing prevents misinformation in output",
840
873
  mem_ctx=mem_ctx,
841
874
  )
842
- if not fact_check: break
875
+ if fact_check is None: continue
843
876
 
844
877
  # ── Step 5: writer ────────────────────────────────────
845
878
  print(f"\n STEP 5/{TOTAL} Writer drafts from verified insights")
@@ -850,7 +883,7 @@ def cmd_demo(args): # noqa: C901
850
883
  learn_insight="3-sentence summaries from verified insights land well with non-technical readers",
851
884
  mem_ctx=mem_ctx,
852
885
  )
853
- if not draft: break
886
+ if draft is None: continue
854
887
 
855
888
  # ── Step 6: devil's advocate ──────────────────────────
856
889
  print(f"\n STEP 6/{TOTAL} Devil's Advocate challenges the draft")
@@ -862,7 +895,7 @@ def cmd_demo(args): # noqa: C901
862
895
  learn_insight="Surfacing counterarguments before final review strengthens the output quality",
863
896
  mem_ctx=mem_ctx,
864
897
  )
865
- if not challenge: break
898
+ if challenge is None: continue
866
899
 
867
900
  # ── Step 7: summarizer ────────────────────────────────
868
901
  print(f"\n STEP 7/{TOTAL} Summarizer distils everything to essence")
@@ -874,7 +907,7 @@ def cmd_demo(args): # noqa: C901
874
907
  learn_insight="A single-sentence TL;DR that survives counterarguments is the most durable insight",
875
908
  mem_ctx=mem_ctx,
876
909
  )
877
- if not summary: break
910
+ if summary is None: continue
878
911
 
879
912
  # ── Step 8: reviewer ──────────────────────────────────
880
913
  print(f"\n STEP 8/{TOTAL} Reviewer gives final score")
@@ -886,7 +919,7 @@ def cmd_demo(args): # noqa: C901
886
919
  learn_insight="Score + strength + improvement is the most actionable review format",
887
920
  mem_ctx=mem_ctx,
888
921
  )
889
- if not review: break
922
+ if review is None: continue
890
923
 
891
924
  print("\n ─────────────────────────────────────────────────")
892
925
  print(f" ✅ Run #{run_count} complete! ({TOTAL} tasks · {TOTAL} lessons written)")
@@ -610,7 +610,9 @@ class AXELServer:
610
610
  confidence=msg.body.get("confidence", 1.0),
611
611
  ))
612
612
  self.event_bus.publish("memory.updated", {
613
- "key": msg.body.get("key"), "source": msg.fr,
613
+ "key": msg.body.get("key"),
614
+ "insight": msg.body.get("insight", ""),
615
+ "source": msg.fr,
614
616
  })
615
617
 
616
618
  # Handle pub/sub publish messages
@@ -600,13 +600,15 @@ function onEvent(d) {
600
600
  setThinking(from, false);
601
601
  updateRateStats();
602
602
 
603
- } else if (type.includes('lesson') || type.includes('learn') || p.t === 'LS') {
603
+ } else if (type === 'memory.updated' || type.includes('lesson') || type.includes('learn')
604
+ || (p.t === 'LS' && (p.body?.key || p.key))) {
604
605
  const from = p.fr || p.from || p.source || '?';
605
606
  const key = p.body?.key || p.key || '';
606
607
  const insight = p.body?.insight || p.insight || '';
608
+ if (!key && !insight) return; // skip empty lesson notifications
607
609
  agentLessons[from] = (agentLessons[from] || 0) + 1;
608
- const label = key ? `<b>${from}</b> learned: <i>${key}</i>` : `<b>${from}</b> learned something new`;
609
- const detail = insight ? '"' + insight.slice(0, 85) + (insight.length > 85 ? '…' : '') + '"' : '';
610
+ const label = `<b>${from}</b> learned: <i>${esc(key || 'new insight')}</i>`;
611
+ const detail = insight ? '"' + insight.slice(0, 100) + (insight.length > 100 ? '…' : '') + '"' : '';
610
612
  addFeed('lesson', '💡', label, detail, { rawType:'LS', from, key, fullInsight: insight });
611
613
  pushMsg(from, `💡 ${key || 'new insight'}`);
612
614
  pulseAgent(from, 'learning');
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "axel-protocol"
7
- version = "2.3.4"
7
+ version = "2.3.6"
8
8
  description = "AXEL — Agent eXchange Language: a universal protocol for multi-LLM networks"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
File without changes
File without changes
File without changes
File without changes
File without changes