Skip to content

Commit

Permalink
initial version
Browse files Browse the repository at this point in the history
  • Loading branch information
ehsan6sha committed Feb 2, 2025
1 parent 400c7c6 commit 59287db
Show file tree
Hide file tree
Showing 9 changed files with 341 additions and 25 deletions.
5 changes: 4 additions & 1 deletion blockchain/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,9 @@ func (bl *FxBlockchain) serve(w http.ResponseWriter, r *http.Request) {
actionFetchContainerLogs: func(from peer.ID, w http.ResponseWriter, r *http.Request) {
bl.handleFetchContainerLogs(r.Context(), from, w, r)
},
actionChatWithAI: func(from peer.ID, w http.ResponseWriter, r *http.Request) {
bl.handleChatWithAI(r.Context(), from, w, r)
},
actionFindBestAndTargetInLogs: func(from peer.ID, w http.ResponseWriter, r *http.Request) {
bl.handleFindBestAndTargetInLogs(r.Context(), from, w, r)
},
Expand Down Expand Up @@ -981,7 +984,7 @@ func (bl *FxBlockchain) authorized(pid peer.ID, action string) bool {
switch action {
case actionReplicateInPool:
return (bl.authorizer == bl.h.ID() || bl.authorizer == "")
case actionBloxFreeSpace, actionAccountFund, actionManifestBatchUpload, actionAssetsBalance, actionGetDatastoreSize, actionGetFolderSize, actionFindBestAndTargetInLogs, actionFetchContainerLogs, actionEraseBlData, actionWifiRemoveall, actionReboot, actionPartition, actionDeleteWifi, actionDisconnectWifi, actionDeleteFulaConfig, actionGetAccount, actionSeeded, actionAccountExists, actionPoolCreate, actionPoolJoin, actionPoolCancelJoin, actionPoolRequests, actionPoolList, actionPoolVote, actionPoolLeave, actionManifestUpload, actionManifestStore, actionManifestAvailable, actionManifestRemove, actionManifestRemoveStorer, actionManifestRemoveStored, actionTransferToMumbai, actionListPlugins, actionListActivePlugins, actionInstallPlugin, actionUninstallPlugin, actionGetInstallStatus, actionGetInstallOutput, actionUpdatePlugin:
case actionBloxFreeSpace, actionAccountFund, actionManifestBatchUpload, actionAssetsBalance, actionGetDatastoreSize, actionGetFolderSize, actionFindBestAndTargetInLogs, actionFetchContainerLogs, actionChatWithAI, actionEraseBlData, actionWifiRemoveall, actionReboot, actionPartition, actionDeleteWifi, actionDisconnectWifi, actionDeleteFulaConfig, actionGetAccount, actionSeeded, actionAccountExists, actionPoolCreate, actionPoolJoin, actionPoolCancelJoin, actionPoolRequests, actionPoolList, actionPoolVote, actionPoolLeave, actionManifestUpload, actionManifestStore, actionManifestAvailable, actionManifestRemove, actionManifestRemoveStorer, actionManifestRemoveStored, actionTransferToMumbai, actionListPlugins, actionListActivePlugins, actionInstallPlugin, actionUninstallPlugin, actionGetInstallStatus, actionGetInstallOutput, actionUpdatePlugin:
bl.authorizedPeersLock.RLock()
_, ok := bl.authorizedPeers[pid]
bl.authorizedPeersLock.RUnlock()
Expand Down
152 changes: 152 additions & 0 deletions blockchain/blox.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package blockchain

import (
"bufio"
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"sync"

"github.com/functionland/go-fula/wap/pkg/wifi"
"github.com/libp2p/go-libp2p/core/network"
Expand All @@ -20,6 +22,49 @@ const (
GB = 1024 * MB
)

type StreamBuffer struct {
mu sync.Mutex
chunks []string
closed bool
err error
}

func NewStreamBuffer() *StreamBuffer {
return &StreamBuffer{
chunks: make([]string, 0),
}
}

func (b *StreamBuffer) AddChunk(chunk string) {
b.mu.Lock()
defer b.mu.Unlock()
if b.closed {
return
}
b.chunks = append(b.chunks, chunk)
}

func (b *StreamBuffer) Close(err error) {
b.mu.Lock()
defer b.mu.Unlock()
b.closed = true
b.err = err
}

func (b *StreamBuffer) GetChunk() (string, error) {
b.mu.Lock()
defer b.mu.Unlock()
if len(b.chunks) > 0 {
chunk := b.chunks[0]
b.chunks = b.chunks[1:]
return chunk, nil
}
if b.closed {
return "", b.err
}
return "", nil // No chunk available yet
}

func (bl *FxBlockchain) BloxFreeSpace(ctx context.Context, to peer.ID) ([]byte, error) {
if bl.allowTransientConnection {
ctx = network.WithUseTransient(ctx, "fx.blockchain")
Expand Down Expand Up @@ -245,6 +290,55 @@ func (bl *FxBlockchain) FetchContainerLogs(ctx context.Context, to peer.ID, r wi
}
}

func (bl *FxBlockchain) ChatWithAI(ctx context.Context, to peer.ID, r wifi.ChatWithAIRequest) (*StreamBuffer, error) {
if bl.allowTransientConnection {
ctx = network.WithUseTransient(ctx, "fx.blockchain")
}

// Encode the request into JSON
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(r); err != nil {
return nil, fmt.Errorf("failed to encode request: %v", err)
}

// Create the HTTP request
req, err := http.NewRequestWithContext(ctx, http.MethodPost, "http://"+to.String()+".invalid/"+actionChatWithAI, &buf)
if err != nil {
return nil, fmt.Errorf("failed to create request: %v", err)
}

resp, err := bl.c.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to send request: %v", err)
}
if resp.StatusCode != http.StatusOK {
defer resp.Body.Close()
bodyBytes, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("unexpected response: %d; body: %s", resp.StatusCode, string(bodyBytes))
}

buffer := NewStreamBuffer() // Create a new StreamBuffer

go func() {
defer resp.Body.Close()
reader := bufio.NewReader(resp.Body)
for {
line, err := reader.ReadString('\n') // Read each chunk line by line
if err != nil {
if err == io.EOF {
buffer.Close(nil) // Close buffer with no error
} else {
buffer.Close(fmt.Errorf("error reading response stream: %v", err))
}
break
}
buffer.AddChunk(line) // Add each chunk to the buffer
}
}()

return buffer, nil // Return the StreamBuffer
}

func (bl *FxBlockchain) FindBestAndTargetInLogs(ctx context.Context, to peer.ID, r wifi.FindBestAndTargetInLogsRequest) ([]byte, error) {

if bl.allowTransientConnection {
Expand Down Expand Up @@ -468,6 +562,64 @@ func (bl *FxBlockchain) handleFetchContainerLogs(ctx context.Context, from peer.

}

func (bl *FxBlockchain) handleChatWithAI(ctx context.Context, from peer.ID, w http.ResponseWriter, r *http.Request) {
log := log.With("action", actionChatWithAI, "from", from)

var req wifi.ChatWithAIRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
log.Error("failed to decode request: %v", err)
http.Error(w, "failed to decode request", http.StatusBadRequest)
return
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusAccepted) // Use StatusAccepted for consistency

flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "Streaming not supported", http.StatusInternalServerError)
return
}

chunks, err := wifi.FetchAIResponse(ctx, req.AIModel, req.UserMessage)
if err != nil {
log.Error("error in fetchAIResponse: %v", err)
http.Error(w, fmt.Sprintf("Error fetching AI response: %v", err), http.StatusInternalServerError)
return
}

log.Debugw("Streaming AI response started", "ai_model", req.AIModel)
defer log.Debugw("Streaming AI response ended", "ai_model", req.AIModel)

for {
select {
case <-ctx.Done(): // Handle client disconnect or cancellation
log.Warn("client disconnected")
return
case chunk, ok := <-chunks:
if !ok {
return // Channel closed
}
response := wifi.ChatWithAIResponse{
Status: true,
Msg: chunk,
}

if err := json.NewEncoder(w).Encode(response); err != nil {
log.Error("failed to write response: %v", err)
errorResponse := wifi.ChatWithAIResponse{
Status: false,
Msg: fmt.Sprintf("Error writing response: %v", err),
}
json.NewEncoder(w).Encode(errorResponse) // Send error as part of stream
flusher.Flush()
return
}
flusher.Flush()
}
}
}

func (bl *FxBlockchain) handleFindBestAndTargetInLogs(ctx context.Context, from peer.ID, w http.ResponseWriter, r *http.Request) {
log := log.With("action", actionFindBestAndTargetInLogs, "from", from)

Expand Down
12 changes: 12 additions & 0 deletions blockchain/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ const (
actionGetInstallOutput = "get-install-output"
actionGetInstallStatus = "get-install-status"
actionUpdatePlugin = "update-plugin"

// AI
actionChatWithAI = "chat-ai"
)

type ReplicateRequest struct {
Expand Down Expand Up @@ -520,6 +523,9 @@ type Blockchain interface {
GetInstallOutput(context.Context, peer.ID, string, string) ([]byte, error)
GetInstallStatus(context.Context, peer.ID, string) ([]byte, error)
UpdatePlugin(context.Context, peer.ID, string) ([]byte, error)

// AI
ChatWithAI(context.Context, peer.ID, wifi.ChatWithAIRequest) (*StreamBuffer, error)
}

var requestTypes = map[string]reflect.Type{
Expand Down Expand Up @@ -574,6 +580,9 @@ var requestTypes = map[string]reflect.Type{
actionGetInstallOutput: reflect.TypeOf(GetInstallOutputRequest{}),
actionGetInstallStatus: reflect.TypeOf(GetInstallStatusRequest{}),
actionUpdatePlugin: reflect.TypeOf(UpdatePluginRequest{}),

// AI
actionChatWithAI: reflect.TypeOf(wifi.ChatWithAIRequest{}),
}

var responseTypes = map[string]reflect.Type{
Expand Down Expand Up @@ -628,4 +637,7 @@ var responseTypes = map[string]reflect.Type{
actionGetInstallOutput: reflect.TypeOf(GetInstallOutputResponse{}),
actionGetInstallStatus: reflect.TypeOf(GetInstallStatusResponse{}),
actionUpdatePlugin: reflect.TypeOf(UpdatePluginResponse{}),

// AI
actionChatWithAI: reflect.TypeOf(wifi.ChatWithAIResponse{}),
}
26 changes: 14 additions & 12 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
module github.com/functionland/go-fula

go 1.21
go 1.22.0

toolchain go1.22.1

require (
github.com/docker/docker v24.0.7+incompatible
github.com/google/uuid v1.6.0
github.com/grandcat/zeroconf v1.0.0
github.com/ipfs-cluster/ipfs-cluster v1.0.8
github.com/ipfs/boxo v0.17.0
Expand All @@ -28,14 +31,16 @@ require (
github.com/mdp/qrterminal v1.0.1
github.com/mr-tron/base58 v1.2.0
github.com/multiformats/go-multiaddr v0.12.2
github.com/multiformats/go-multibase v0.2.0
github.com/multiformats/go-multicodec v0.9.0
github.com/multiformats/go-multihash v0.2.3
github.com/multiformats/go-varint v0.0.7
github.com/sony/gobreaker v0.5.0
github.com/tyler-smith/go-bip39 v1.1.0
github.com/urfave/cli/v2 v2.27.1
go.uber.org/ratelimit v0.3.0
golang.org/x/crypto v0.21.0
golang.org/x/sync v0.6.0
golang.org/x/crypto v0.32.0
golang.org/x/sync v0.10.0
gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v3 v3.0.1
)
Expand All @@ -49,7 +54,7 @@ require (
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/sys v0.29.0 // indirect
gotest.tools/v3 v3.4.0 // indirect
)

Expand Down Expand Up @@ -110,7 +115,6 @@ require (
github.com/golang/snappy v0.0.4 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
Expand Down Expand Up @@ -193,7 +197,6 @@ require (
github.com/multiformats/go-base36 v0.2.0 // indirect
github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
github.com/multiformats/go-multibase v0.2.0 // indirect
github.com/multiformats/go-multistream v0.5.0 // indirect
github.com/onsi/ginkgo/v2 v2.13.2 // indirect
github.com/opencontainers/runtime-spec v1.1.0 // indirect
Expand Down Expand Up @@ -233,7 +236,6 @@ require (
github.com/rs/cors v1.10.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/samber/lo v1.39.0 // indirect
github.com/sony/gobreaker v0.5.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
Expand Down Expand Up @@ -268,12 +270,12 @@ require (
go.uber.org/zap v1.26.0 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect
golang.org/x/mobile v0.0.0-20240320162201-c76e57eead38 // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/mobile v0.0.0-20250106192035-c31d5b91ecc3 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/oauth2 v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.19.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.29.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
gonum.org/v1/gonum v0.14.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
Expand Down
Loading

0 comments on commit 59287db

Please sign in to comment.