quinkgl 0.1.2__tar.gz → 0.1.16__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 (51) hide show
  1. {quinkgl-0.1.2/src/quinkgl.egg-info → quinkgl-0.1.16}/PKG-INFO +1 -1
  2. {quinkgl-0.1.2 → quinkgl-0.1.16}/pyproject.toml +1 -1
  3. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/network/gossip_community.py +59 -19
  4. {quinkgl-0.1.2 → quinkgl-0.1.16/src/quinkgl.egg-info}/PKG-INFO +1 -1
  5. {quinkgl-0.1.2 → quinkgl-0.1.16}/MANIFEST.in +0 -0
  6. {quinkgl-0.1.2 → quinkgl-0.1.16}/README.md +0 -0
  7. {quinkgl-0.1.2 → quinkgl-0.1.16}/requirements.txt +0 -0
  8. {quinkgl-0.1.2 → quinkgl-0.1.16}/setup.cfg +0 -0
  9. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/__init__.py +0 -0
  10. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/aggregation/__init__.py +0 -0
  11. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/aggregation/base.py +0 -0
  12. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/aggregation/fedavg.py +0 -0
  13. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/config.py +0 -0
  14. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/core/__init__.py +0 -0
  15. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/core/context.py +0 -0
  16. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/core/dummy_model.py +0 -0
  17. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/core/model_interface.py +0 -0
  18. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/core/node.py +0 -0
  19. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/data/__init__.py +0 -0
  20. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/data/datasets.py +0 -0
  21. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/gossip/__init__.py +0 -0
  22. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/gossip/orchestrator.py +0 -0
  23. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/gossip/protocol.py +0 -0
  24. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/models/__init__.py +0 -0
  25. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/models/base.py +0 -0
  26. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/models/pytorch.py +0 -0
  27. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/models/tensorflow.py +0 -0
  28. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/network/__init__.py +0 -0
  29. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/network/connection_manager.py +0 -0
  30. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/network/gossip_node.py +0 -0
  31. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/network/gossip_pb2.py +0 -0
  32. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/network/gossip_pb2_grpc.py +0 -0
  33. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/network/grpc_node.py +0 -0
  34. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/network/ipv8_community.py +0 -0
  35. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/network/ipv8_manager.py +0 -0
  36. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/network/model_serializer.py +0 -0
  37. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/network/tunnel_client.py +0 -0
  38. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/network/tunnel_pb2.py +0 -0
  39. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/network/tunnel_pb2_grpc.py +0 -0
  40. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/storage/__init__.py +0 -0
  41. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/storage/model_store.py +0 -0
  42. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/topology/__init__.py +0 -0
  43. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/topology/base.py +0 -0
  44. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/topology/cyclon.py +0 -0
  45. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/topology/random.py +0 -0
  46. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/topology/sampler.py +0 -0
  47. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl/utils/serialization.py +0 -0
  48. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl.egg-info/SOURCES.txt +0 -0
  49. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl.egg-info/dependency_links.txt +0 -0
  50. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl.egg-info/requires.txt +0 -0
  51. {quinkgl-0.1.2 → quinkgl-0.1.16}/src/quinkgl.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quinkgl
3
- Version: 0.1.2
3
+ Version: 0.1.16
4
4
  Summary: A decentralized gossip learning framework for P2P edge intelligence
5
5
  Author-email: Ali Seyhan <aliseyhan@posta.mu.edu.tr>, Baki Turhan <bakiturhan@posta.mu.edu.tr>
6
6
  Project-URL: Homepage, https://github.com/aliseyhann/QuinkGL-Gossip-Learning-Framework
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "quinkgl"
7
- version = "0.1.2"
7
+ version = "0.1.16"
8
8
  description = "A decentralized gossip learning framework for P2P edge intelligence"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -8,6 +8,7 @@ CHUNKED TRANSFER: Large model updates are split into chunks
8
8
  to work around UDP MTU limits (~1400 bytes).
9
9
  """
10
10
 
11
+ import asyncio
11
12
  import time
12
13
  import logging
13
14
  import hashlib
@@ -30,7 +31,7 @@ MAX_INCOMING_MESSAGE_SIZE = 150 * 1024 * 1024
30
31
  # Chunk size for large model transfers
31
32
  # Using 60KB which is below UDP's 65507 byte limit but reduces packet count significantly
32
33
  # A 5.8MB model = ~100 chunks instead of ~5800 chunks
33
- CHUNK_SIZE = 60000
34
+ CHUNK_SIZE = 10000 # 10KB chunks - smaller for better NAT traversal
34
35
 
35
36
  # Timeout for incomplete transfers (300 seconds - increased for Colab/slow networks)
36
37
  CHUNK_TRANSFER_TIMEOUT = 300
@@ -521,6 +522,7 @@ class GossipLearningCommunity(Community):
521
522
  model_version=payload.model_version
522
523
  )
523
524
  self.known_peers[payload.node_id] = peer_info
525
+ print(f" 🔗 Peer adresi: {payload.node_id} @ {peer.address}")
524
526
  logger.info(f"✅ Discovered compatible peer: {payload.node_id}")
525
527
 
526
528
  if self.on_peer_discovered_callback:
@@ -590,22 +592,27 @@ class GossipLearningCommunity(Community):
590
592
 
591
593
  Buffers chunks and triggers model processing when all chunks are received.
592
594
  """
593
- transfer_id = payload.transfer_id
595
+ print(f" [DEBUG] on_model_chunk called: chunk {payload.chunk_index}/{payload.total_chunks}")
596
+ transfer_id = f"{payload.sender_id}:{payload.round_number}" # Use sender:round as key
594
597
 
595
- # Log chunk receipt (only log first, last, and every 100th chunk to reduce noise)
598
+ # Log chunk receipt with visible print statements
596
599
  if payload.chunk_index == 0:
600
+ print(f" 📥 Chunk alımı başladı: {payload.sender_id} ({payload.total_chunks} chunk)")
597
601
  logger.info(
598
602
  f"[NET] Receiving chunked model from {payload.sender_id} "
599
603
  f"(transfer={transfer_id[:8]}..., chunks={payload.total_chunks}, "
600
604
  f"round={payload.round_number})"
601
605
  )
602
- elif payload.chunk_index == payload.total_chunks - 1:
606
+ elif (payload.chunk_index + 1) % 50 == 0 or payload.chunk_index == payload.total_chunks - 1:
607
+ progress = (payload.chunk_index + 1) / payload.total_chunks * 100
608
+ print(f" 📥 Alındı: {payload.chunk_index + 1}/{payload.total_chunks} ({progress:.0f}%)")
609
+
610
+ if payload.chunk_index == payload.total_chunks - 1:
603
611
  logger.info(f"[NET] Received final chunk {payload.chunk_index + 1}/{payload.total_chunks} from {payload.sender_id}")
604
- elif payload.chunk_index % 100 == 0:
605
- logger.debug(f"[NET] Chunk progress: {payload.chunk_index + 1}/{payload.total_chunks} from {payload.sender_id}")
606
612
 
607
613
  # Create or get buffer for this transfer
608
614
  if transfer_id not in self._chunk_buffers:
615
+ print(f" 🆕 Yeni buffer oluşturuldu: {transfer_id[:8]}... (chunk {payload.chunk_index})")
609
616
  self._chunk_buffers[transfer_id] = ChunkBuffer(
610
617
  transfer_id=transfer_id,
611
618
  sender_id=payload.sender_id,
@@ -625,6 +632,14 @@ class GossipLearningCommunity(Community):
625
632
  # Update peer last seen
626
633
  if payload.sender_id in self.known_peers:
627
634
  self.known_peers[payload.sender_id].update_seen()
635
+
636
+ # NAT keepalive: Send heartbeat every 5 chunks to keep NAT hole open
637
+ if payload.chunk_index % 5 == 0:
638
+ peer_info = self.known_peers[payload.sender_id]
639
+ self.ez_send(peer_info.peer, HeartbeatPayload(
640
+ self.node_id,
641
+ self._heartbeat_sequence
642
+ ))
628
643
 
629
644
  # If all chunks received, reassemble and process
630
645
  if is_complete:
@@ -658,21 +673,38 @@ class GossipLearningCommunity(Community):
658
673
 
659
674
  # Call callback
660
675
  if self.on_model_update_callback:
661
- await self.on_model_update_callback(
662
- sender_id=payload.sender_id,
663
- weights=weights,
664
- sample_count=buffer.sample_count,
665
- round_number=buffer.round_number,
666
- loss=buffer.loss,
667
- accuracy=buffer.accuracy
668
- )
676
+ print(f" ✅ Model reassemble tamamlandı, callback çağrılıyor...")
677
+ try:
678
+ await self.on_model_update_callback(
679
+ sender_id=payload.sender_id,
680
+ weights=weights,
681
+ sample_count=buffer.sample_count,
682
+ round_number=buffer.round_number,
683
+ loss=buffer.loss,
684
+ accuracy=buffer.accuracy
685
+ )
686
+ print(f" ✅ Callback başarılı!")
687
+ except Exception as cb_err:
688
+ print(f" ❌ Callback hatası: {cb_err}")
689
+ import traceback
690
+ traceback.print_exc()
691
+ else:
692
+ print(f" ⚠️ Callback yok!")
669
693
 
670
694
  except Exception as e:
671
695
  logger.error(f"Failed to reassemble/process chunked model from {payload.sender_id}: {e}")
672
696
  if transfer_id in self._chunk_buffers:
673
697
  del self._chunk_buffers[transfer_id]
674
-
675
- def send_model_update(
698
+ else:
699
+ # Debug: if this is the final chunk but buffer is not complete, log missing chunks
700
+ if payload.chunk_index == payload.total_chunks - 1:
701
+ actual_chunks = len(buffer.chunks)
702
+ print(f" ⚠️ DEBUG: Son chunk alındı ama buffer complete değil!")
703
+ print(f" Buffer: {actual_chunks}/{buffer.total_chunks} chunks")
704
+ missing = [i for i in range(buffer.total_chunks) if i not in buffer.chunks][:10]
705
+ print(f" Eksik chunk indexleri: {missing}...")
706
+
707
+ async def send_model_update(
676
708
  self,
677
709
  target_node_id: str,
678
710
  weights: Any,
@@ -731,6 +763,9 @@ class GossipLearningCommunity(Community):
731
763
  transfer_id = str(uuid.uuid4())
732
764
  total_chunks = (len(weights_bytes) + CHUNK_SIZE - 1) // CHUNK_SIZE
733
765
 
766
+ # Debug: show target peer address
767
+ print(f" 🎯 Target: {target_node_id} @ {peer_info.peer.address}")
768
+
734
769
  logger.info(
735
770
  f"[NET] Sending chunked model to {target_node_id} "
736
771
  f"(transfer={transfer_id[:8]}..., size={len(weights_bytes)} bytes, chunks={total_chunks})"
@@ -756,10 +791,15 @@ class GossipLearningCommunity(Community):
756
791
 
757
792
  self.ez_send(peer_info.peer, chunk_payload)
758
793
 
759
- # Rate limiting: 5ms delay between chunks to prevent UDP buffer overflow
760
- # 98 chunks * 5ms = ~500ms per transfer, acceptable for model updates
794
+ # Progress logging every 50 chunks (with 10KB chunks, we have ~230 chunks)
795
+ if (i + 1) % 50 == 0 or i == total_chunks - 1:
796
+ progress = (i + 1) / total_chunks * 100
797
+ print(f" 📦 Chunk {i + 1}/{total_chunks} ({progress:.0f}%)")
798
+
799
+ # Rate limiting: 200ms delay between chunks
800
+ # Using asyncio.sleep so event loop can process incoming packets
761
801
  if i < total_chunks - 1: # Don't sleep after the last chunk
762
- time.sleep(0.005)
802
+ await asyncio.sleep(0.2)
763
803
 
764
804
  logger.info(f"[NET] Sent {total_chunks} chunks to {target_node_id}")
765
805
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quinkgl
3
- Version: 0.1.2
3
+ Version: 0.1.16
4
4
  Summary: A decentralized gossip learning framework for P2P edge intelligence
5
5
  Author-email: Ali Seyhan <aliseyhan@posta.mu.edu.tr>, Baki Turhan <bakiturhan@posta.mu.edu.tr>
6
6
  Project-URL: Homepage, https://github.com/aliseyhann/QuinkGL-Gossip-Learning-Framework
File without changes
File without changes
File without changes
File without changes
File without changes