|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| module sov_knowledge
|
| use, intrinsic :: iso_c_binding, only: c_int64_t, c_ptr, c_f_pointer, &
|
| c_loc, c_size_t, c_null_ptr, c_char, c_associated
|
| use, intrinsic :: iso_fortran_env, only: int64, real64, int8
|
| use bob_kinds
|
| use bob_worm, only: bob_worm_chain, blake3_hash_string
|
| implicit none
|
| private
|
|
|
| integer, parameter, public :: KB_EMBED_DIM = 64
|
| integer, parameter, public :: KB_ID_LEN = 64
|
| integer, parameter, public :: KB_SIG_LEN = 64
|
| integer, parameter, public :: KB_DEFAULT_CAP = 1024
|
|
|
| real(dp), parameter :: PHI_INV = 0.6180339887498948482_dp
|
| real(dp), parameter :: EPS = 1.0e-15_dp
|
|
|
|
|
|
|
|
|
| type, public :: knowledge_chunk
|
| character(len=KB_ID_LEN) :: chunk_id = ''
|
| character(len=KB_SIG_LEN) :: source_sig = ''
|
| integer(int64) :: created_at = 0_int64
|
| real(dp), allocatable :: embedding(:)
|
| character(len=:), allocatable :: content
|
| logical :: is_verified = .false.
|
| end type knowledge_chunk
|
|
|
|
|
|
|
|
|
| type, public :: knowledge_store
|
| type(knowledge_chunk), allocatable :: chunks(:)
|
| integer :: count = 0
|
| integer :: capacity = KB_DEFAULT_CAP
|
| type(bob_worm_chain) :: worm
|
| logical :: initialized = .false.
|
| contains
|
| procedure, public :: init => knowledge_init
|
| procedure, public :: append => knowledge_append
|
| procedure, public :: search => knowledge_search
|
| procedure, public :: verify => knowledge_verify
|
| procedure, public :: trust_score => knowledge_trust_score
|
| procedure, public :: load_from_worm => knowledge_load_from_worm
|
| procedure, public :: destroy => knowledge_destroy
|
| end type knowledge_store
|
|
|
|
|
| type(knowledge_store), public, save :: sovereign_kb
|
| logical, public, save :: kb_initialized = .false.
|
|
|
| public :: cosine_sim
|
| public :: generate_embedding
|
| public :: knowledge_tau
|
| public :: knowledge_penalty_scale
|
| public :: ensure_sovereign_kb
|
|
|
|
|
| public :: sov_knowledge_init
|
| public :: sov_knowledge_append
|
| public :: sov_knowledge_search
|
| public :: sov_knowledge_verify
|
| public :: sov_knowledge_count
|
| public :: sov_knowledge_tau
|
|
|
| contains
|
|
|
|
|
|
|
|
|
| pure function cosine_sim(a, b) result(sim)
|
| real(dp), intent(in) :: a(:), b(:)
|
| real(dp) :: sim, dot_prod, norm_a, norm_b
|
| integer :: n, i
|
|
|
| n = min(size(a), size(b))
|
| if (n <= 0) then
|
| sim = 0.0_dp
|
| return
|
| end if
|
|
|
| dot_prod = 0.0_dp
|
| norm_a = 0.0_dp
|
| norm_b = 0.0_dp
|
| do i = 1, n
|
| dot_prod = dot_prod + a(i) * b(i)
|
| norm_a = norm_a + a(i) * a(i)
|
| norm_b = norm_b + b(i) * b(i)
|
| end do
|
| norm_a = sqrt(norm_a)
|
| norm_b = sqrt(norm_b)
|
| if (norm_a < EPS .or. norm_b < EPS) then
|
| sim = 0.0_dp
|
| else
|
| sim = dot_prod / (norm_a * norm_b)
|
| end if
|
| end function cosine_sim
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| subroutine generate_embedding(content, embed)
|
| character(len=*), intent(in) :: content
|
| real(dp), allocatable, intent(out) :: embed(:)
|
| integer :: i, n, b0, b1
|
| integer :: c, c_prev
|
| real(dp) :: w, nrm
|
|
|
| allocate(embed(KB_EMBED_DIM))
|
| embed = 0.0_dp
|
| n = len_trim(content)
|
| if (n <= 0) then
|
| embed(1) = 1.0_dp
|
| return
|
| end if
|
|
|
| c_prev = 0
|
| do i = 1, n
|
| c = iachar(content(i:i))
|
| w = PHI_INV ** mod(i - 1, 32)
|
| b0 = mod(c * 31 + i, KB_EMBED_DIM) + 1
|
| b1 = mod(c * 17 + c_prev * 13 + i * 7, KB_EMBED_DIM) + 1
|
| embed(b0) = embed(b0) + w
|
| embed(b1) = embed(b1) + w * PHI_INV
|
| c_prev = c
|
| end do
|
|
|
| nrm = sqrt(sum(embed * embed))
|
| if (nrm > EPS) then
|
| embed = embed / nrm
|
| else
|
| embed(1) = 1.0_dp
|
| end if
|
| end subroutine generate_embedding
|
|
|
|
|
|
|
|
|
|
|
| pure function knowledge_tau(tau_0, k_hits) result(tau_k)
|
| real(dp), intent(in) :: tau_0
|
| integer, intent(in) :: k_hits
|
| real(dp) :: tau_k
|
| integer :: k
|
|
|
| k = max(0, k_hits)
|
| tau_k = tau_0 * (PHI_INV ** k)
|
| tau_k = max(tau_k, 1.0e-12_dp)
|
| end function knowledge_tau
|
|
|
|
|
|
|
|
|
|
|
| pure function knowledge_penalty_scale(n_total, n_unverified) result(scale)
|
| integer, intent(in) :: n_total, n_unverified
|
| real(dp) :: scale, penalty
|
|
|
| if (n_total <= 0) then
|
| scale = 1.0_dp
|
| return
|
| end if
|
| penalty = real(n_unverified, dp) / real(n_total, dp)
|
| scale = 1.0_dp - PHI_INV * penalty
|
| scale = max(scale, PHI_INV)
|
| end function knowledge_penalty_scale
|
|
|
|
|
|
|
|
|
| pure function digest_to_hex(b) result(hex)
|
| integer(i8), intent(in) :: b(32)
|
| character(len=64) :: hex
|
| character(len=16), parameter :: H = '0123456789abcdef'
|
| integer :: i, hi, lo
|
| do i = 1, 32
|
| hi = ishft(iand(int(b(i), kind=4), 240), -4) + 1
|
| lo = iand(int(b(i), kind=4), 15) + 1
|
| hex(2*i-1:2*i-1) = H(hi:hi)
|
| hex(2*i:2*i) = H(lo:lo)
|
| end do
|
| end function digest_to_hex
|
|
|
| pure function bytes_to_hex64(b, n) result(hex)
|
| integer(i8), intent(in) :: b(:)
|
| integer, intent(in) :: n
|
| character(len=64) :: hex
|
| character(len=16), parameter :: H = '0123456789abcdef'
|
| integer :: i, m, hi, lo
|
| hex = repeat('0', 64)
|
| m = min(n, 32)
|
| do i = 1, m
|
| hi = ishft(iand(int(b(i), kind=4), 240), -4) + 1
|
| lo = iand(int(b(i), kind=4), 15) + 1
|
| hex(2*i-1:2*i-1) = H(hi:hi)
|
| hex(2*i:2*i) = H(lo:lo)
|
| end do
|
| end function bytes_to_hex64
|
|
|
|
|
|
|
|
|
| subroutine knowledge_init(self, capacity)
|
| class(knowledge_store), intent(inout) :: self
|
| integer, intent(in), optional :: capacity
|
| integer :: cap
|
|
|
| cap = KB_DEFAULT_CAP
|
| if (present(capacity)) cap = max(1, capacity)
|
|
|
| if (allocated(self%chunks)) deallocate(self%chunks)
|
| allocate(self%chunks(cap))
|
| self%count = 0
|
| self%capacity = cap
|
| call self%worm%init(capacity=max(cap, 256))
|
| self%initialized = .true.
|
| end subroutine knowledge_init
|
|
|
| subroutine ensure_sovereign_kb()
|
| if (.not. kb_initialized) then
|
| call sovereign_kb%init(KB_DEFAULT_CAP)
|
| kb_initialized = .true.
|
| end if
|
| end subroutine ensure_sovereign_kb
|
|
|
|
|
|
|
|
|
| subroutine knowledge_append(self, content, source_key)
|
| class(knowledge_store), intent(inout) :: self
|
| character(len=*), intent(in) :: content
|
| character(len=*), intent(in) :: source_key
|
|
|
| type(knowledge_chunk) :: new_chunk
|
| integer(i8) :: digest(32), sig_digest(32)
|
| character(len=:), allocatable :: material, sig_material
|
| integer :: n
|
|
|
| if (.not. self%initialized) call self%init()
|
|
|
| n = len_trim(content)
|
| material = content(1:n) // '|' // trim(source_key)
|
| call blake3_hash_string(material, digest)
|
| new_chunk%chunk_id = digest_to_hex(digest)
|
|
|
|
|
|
|
| sig_material = trim(source_key) // '|' // content(1:n)
|
| call blake3_hash_string(sig_material, sig_digest)
|
| new_chunk%source_sig = digest_to_hex(sig_digest)
|
|
|
| new_chunk%created_at = int(self%worm%height(), int64)
|
| call generate_embedding(content(1:n), new_chunk%embedding)
|
| new_chunk%content = content(1:n)
|
| new_chunk%is_verified = .true.
|
|
|
|
|
| call self%worm%seal('KNOWLEDGE', new_chunk%chunk_id, int(n, int64))
|
|
|
| if (self%count >= self%capacity) call knowledge_resize(self, self%capacity * 2)
|
| self%count = self%count + 1
|
| self%chunks(self%count) = new_chunk
|
| end subroutine knowledge_append
|
|
|
| subroutine knowledge_resize(self, new_cap)
|
| class(knowledge_store), intent(inout) :: self
|
| integer, intent(in) :: new_cap
|
| type(knowledge_chunk), allocatable :: temp(:)
|
| integer :: n
|
|
|
| n = self%count
|
| allocate(temp(new_cap))
|
| if (n > 0) temp(1:n) = self%chunks(1:n)
|
| call move_alloc(temp, self%chunks)
|
| self%capacity = new_cap
|
| end subroutine knowledge_resize
|
|
|
|
|
|
|
|
|
| subroutine knowledge_search(self, query, k, top_k, n_out)
|
| class(knowledge_store), intent(in) :: self
|
| character(len=*), intent(in) :: query
|
| integer, intent(in) :: k
|
| type(knowledge_chunk), allocatable, intent(out) :: top_k(:)
|
| integer, intent(out) :: n_out
|
|
|
| real(dp), allocatable :: query_embed(:), scores(:)
|
| integer :: i, j, max_idx, n_take
|
| real(dp) :: best
|
|
|
| n_out = 0
|
| if (.not. self%initialized .or. self%count <= 0) then
|
| allocate(top_k(0))
|
| return
|
| end if
|
|
|
| call generate_embedding(query, query_embed)
|
| allocate(scores(self%count))
|
| do i = 1, self%count
|
| if (allocated(self%chunks(i)%embedding)) then
|
| scores(i) = cosine_sim(query_embed, self%chunks(i)%embedding)
|
| else
|
| scores(i) = -huge(0.0_dp)
|
| end if
|
| end do
|
|
|
| n_take = min(k, self%count)
|
| allocate(top_k(n_take))
|
| do i = 1, n_take
|
| max_idx = 1
|
| best = scores(1)
|
| do j = 2, self%count
|
| if (scores(j) > best) then
|
| best = scores(j)
|
| max_idx = j
|
| end if
|
| end do
|
| top_k(i) = self%chunks(max_idx)
|
| scores(max_idx) = -huge(0.0_dp)
|
| end do
|
| n_out = n_take
|
| deallocate(query_embed, scores)
|
| end subroutine knowledge_search
|
|
|
|
|
|
|
|
|
| logical function knowledge_verify(self, chunk_id) result(valid)
|
| class(knowledge_store), intent(in) :: self
|
| character(len=*), intent(in) :: chunk_id
|
| integer :: i
|
| integer(i8) :: digest(32)
|
| character(len=64) :: recomputed
|
| character(len=:), allocatable :: material
|
|
|
| valid = .false.
|
| if (.not. self%initialized) return
|
|
|
| do i = 1, self%count
|
| if (trim(self%chunks(i)%chunk_id) == trim(chunk_id)) then
|
| if (.not. self%chunks(i)%is_verified) return
|
| if (.not. allocated(self%chunks(i)%content)) return
|
| if (len_trim(self%chunks(i)%source_sig) < 16) return
|
|
|
|
|
| material = self%chunks(i)%content
|
| call blake3_hash_string(material, digest)
|
| recomputed = digest_to_hex(digest)
|
| valid = self%chunks(i)%is_verified &
|
| .and. len_trim(self%chunks(i)%chunk_id) == 64 &
|
| .and. len_trim(recomputed) == 64 &
|
| .and. self%worm%verify()
|
| return
|
| end if
|
| end do
|
| end function knowledge_verify
|
|
|
| pure function knowledge_trust_score(self) result(score)
|
| class(knowledge_store), intent(in) :: self
|
| real(dp) :: score
|
| integer :: i, ok
|
|
|
| if (self%count <= 0) then
|
| score = 1.0_dp
|
| return
|
| end if
|
| ok = 0
|
| do i = 1, self%count
|
| if (self%chunks(i)%is_verified) ok = ok + 1
|
| end do
|
| score = real(ok, dp) / real(self%count, dp)
|
| end function knowledge_trust_score
|
|
|
|
|
|
|
|
|
|
|
| subroutine knowledge_load_from_worm(self)
|
| class(knowledge_store), intent(inout) :: self
|
| if (.not. self%initialized) call self%init()
|
|
|
|
|
| end subroutine knowledge_load_from_worm
|
|
|
| subroutine knowledge_destroy(self)
|
| class(knowledge_store), intent(inout) :: self
|
| if (allocated(self%chunks)) deallocate(self%chunks)
|
| call self%worm%destroy()
|
| self%count = 0
|
| self%capacity = 0
|
| self%initialized = .false.
|
| end subroutine knowledge_destroy
|
|
|
|
|
|
|
|
|
| subroutine sov_knowledge_init(capacity) &
|
| bind(C, name="sov_knowledge_init")
|
| integer(c_int64_t), intent(in), value :: capacity
|
| call ensure_sovereign_kb()
|
| if (capacity > 0) call sovereign_kb%init(int(capacity))
|
| end subroutine sov_knowledge_init
|
|
|
| subroutine sov_knowledge_append(content_ptr, content_len, key_ptr, key_len) &
|
| bind(C, name="sov_knowledge_append")
|
| type(c_ptr), intent(in), value :: content_ptr, key_ptr
|
| integer(c_int64_t), intent(in), value :: content_len, key_len
|
| character(kind=c_char), pointer :: cbuf(:), kbuf(:)
|
| character(len=:), allocatable :: content, key
|
| integer :: i, nc, nk
|
|
|
| call ensure_sovereign_kb()
|
| nc = max(0, int(content_len))
|
| nk = max(0, int(key_len))
|
| if (nc <= 0) return
|
|
|
| call c_f_pointer(content_ptr, cbuf, [nc])
|
| allocate(character(len=nc) :: content)
|
| do i = 1, nc
|
| content(i:i) = transfer(cbuf(i), ' ')
|
| end do
|
|
|
| if (nk > 0 .and. c_associated(key_ptr)) then
|
| call c_f_pointer(key_ptr, kbuf, [nk])
|
| allocate(character(len=nk) :: key)
|
| do i = 1, nk
|
| key(i:i) = transfer(kbuf(i), ' ')
|
| end do
|
| else
|
| key = 'SOVEREIGN'
|
| end if
|
|
|
| call sovereign_kb%append(content, key)
|
| end subroutine sov_knowledge_append
|
|
|
| function sov_knowledge_search(query_ptr, query_len, k) result(n_hits) &
|
| bind(C, name="sov_knowledge_search")
|
| type(c_ptr), intent(in), value :: query_ptr
|
| integer(c_int64_t), intent(in), value :: query_len, k
|
| integer(c_int64_t) :: n_hits
|
| character(kind=c_char), pointer :: qbuf(:)
|
| character(len=:), allocatable :: query
|
| type(knowledge_chunk), allocatable :: hits(:)
|
| integer :: i, nq, n_out
|
|
|
| call ensure_sovereign_kb()
|
| n_hits = 0
|
| nq = max(0, int(query_len))
|
| if (nq <= 0) return
|
| call c_f_pointer(query_ptr, qbuf, [nq])
|
| allocate(character(len=nq) :: query)
|
| do i = 1, nq
|
| query(i:i) = transfer(qbuf(i), ' ')
|
| end do
|
| call sovereign_kb%search(query, max(1, int(k)), hits, n_out)
|
| n_hits = int(n_out, c_int64_t)
|
| end function sov_knowledge_search
|
|
|
| function sov_knowledge_verify(id_ptr, id_len) result(ok) &
|
| bind(C, name="sov_knowledge_verify")
|
| type(c_ptr), intent(in), value :: id_ptr
|
| integer(c_int64_t), intent(in), value :: id_len
|
| integer(c_int64_t) :: ok
|
| character(kind=c_char), pointer :: ibuf(:)
|
| character(len=:), allocatable :: chunk_id
|
| integer :: i, n
|
|
|
| call ensure_sovereign_kb()
|
| ok = 0
|
| n = max(0, int(id_len))
|
| if (n <= 0) return
|
| call c_f_pointer(id_ptr, ibuf, [n])
|
| allocate(character(len=n) :: chunk_id)
|
| do i = 1, n
|
| chunk_id(i:i) = transfer(ibuf(i), ' ')
|
| end do
|
| if (sovereign_kb%verify(chunk_id)) ok = 1
|
| end function sov_knowledge_verify
|
|
|
| function sov_knowledge_count() result(n) &
|
| bind(C, name="sov_knowledge_count")
|
| integer(c_int64_t) :: n
|
| call ensure_sovereign_kb()
|
| n = int(sovereign_kb%count, c_int64_t)
|
| end function sov_knowledge_count
|
|
|
| function sov_knowledge_tau(tau_0, k_hits) result(tau_k) &
|
| bind(C, name="sov_knowledge_tau")
|
| real(dp), intent(in), value :: tau_0
|
| integer(c_int64_t), intent(in), value :: k_hits
|
| real(dp) :: tau_k
|
| tau_k = knowledge_tau(tau_0, int(k_hits))
|
| end function sov_knowledge_tau
|
|
|
| end module sov_knowledge
|
|
|